SpringMVC源码学习(一) - DispatcherSerlet和相关组件

时间:2022-07-26
本文章向大家介绍SpringMVC源码学习(一) - DispatcherSerlet和相关组件,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在前几期中我们说SpringBoot中提供了很多onRefresh方法方法,其中有两个是包含创建Servlet容器的。至于剩余的实现我们当时说可以做一些不需要不需要开端口或者不需要Servlet的那种情况。比如我们要写一个程序做数据库同步,我们只是想使用Spring,不想使用SpringMVC。那么我们就可以在创建SpringBoot的初始化的时候指定SpringApplication的类型,如图所示。

在一般的web项目中,我们还是创建的Servlet容器服务。也就是在onRefresh方法中会创建webServer。而webServer最终离不开Servlet,也就是说在创建webServer的时候就会初始化Servlet,在项目中我们主要采用DispatcherServlet作为开发,当然这不是必然。我们可以自定义或者重写DispatcherServlet,这样的话就相当于我们只使用Spring的IOC部分并重新定义了MVC部分。

这期我们还是以DispatcherServlet为核心进行分析。之前我们在学习onRefresh方法的时候已经提示过SpringMVC的整合原理。大概的回顾一下就是在Spring通过类上注解扫描的时候就已经将DispatcherServlet注册到BeanDefineMap中了。最后通过getBean方法进行Bean的实例化将Bean注册到ObjectMap中。但是这时候DispatcherServlet的真正的初始化,Spring在类实例化之后会发布事件。DispaccherServlet的父类FrameWorkServlet通过子类实现ApplicationListener接口来监听Spring事件,并将Spring相关的组件逐个注入到DipatcherServlet中。这期我就学习这里到底都注入了什么。

通过代码,我们发现这里的doApplicationEvent方法就是主要的核心代码。而且还加锁了。这里为什么要加锁?

仔细想一下我们之前说过Spring事件机制。它的本质是一个广播,他的调用方法的命名就是广播,而且它是全部广播,所以只要实现了ApplcationListener接口的类都会被调用onApplicatonEvent方法,如果我们在代码中设置几个广播消息就会在此产生并发情况。可能MVC还没初始化结束。那么我们的MVC就混乱了。

除此之外我们看到这里使用了protected修饰,那么我就需要子类DispatcherServlet中看看究竟做了哪些事情了。其实在源码学习的过程中我们也能感觉到想父类或者抽象方法等都有一个作用就是调度。这些都是在我们需要学习的细节。

在DispatcherServlet中我们看到传入的是我们的上下文,也就是IOC容器。

    protected void onRefresh(ApplicationContext context) {
        //初始化
        this.initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        //通过ioc获取文件解析器
        this.initMultipartResolver(context);
        //语言解析器
        this.initLocaleResolver(context);
        //初始化主题
        this.initThemeResolver(context);
        //初始化控制其
        this.initHandlerMappings(context);
        //初始化控制器适配器
        this.initHandlerAdapters(context);
        //初始化拦截失败解析器
        this.initHandlerExceptionResolvers(context);
        //初始化静态资源处理器
        this.initRequestToViewNameTranslator(context);
        //初始化化视图解析器
        this.initViewResolvers(context);
        //初始化flash处理器
        this.initFlashMapManager(context);
    }

在文件解析器中,我们发现SpringMvc首先从IOC中获取。至于解析这里就不说了,大概就是对request请求的处理。

在语言解析器的初始化中,如果没有设置的话springMvc就会设置一个默认值。

顺便我们发现我们的语言处理器是否有点眼熟。这就是我们国际化用到的呀,是否就不用解释了。session就是在会话中使用相同的语言,cookie则是通过cookie来解析使用何种语言,fixed就是固定了。那么我们看一下这里的默认值是多少。

通过代码跟踪,发现如果配置文件没有设置,就直接返回空了。但是是这样吗?通过仔细查看,发现这里有默认配置。

那么在initThemeResolver方法中都做了哪些工作?通过代码跟踪,发现好像也没做什么哦!

initHandlerMappings方法是初始化拦截器的,那么它是怎么做的?

我们发现这里并没有拦截器接口HandlerInterceptor的踪迹。那么控制器是hander是如何初始化的?通过查询相关资料我们发现在实现了HandlerMapping接口的类具有方法

我们看看这个方法是做什么的。在initApplicationContext方法中,我们发现如下代码。

    protected void detectHandlers() throws BeansException {
        ApplicationContext applicationContext = this.obtainApplicationContext();
       //获取所有的bean的名称
        String[] beanNames = this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class);
        String[] var3 = beanNames;
        int var4 = beanNames.length;
        for(int var5 = 0; var5 < var4; ++var5) {
            String beanName = var3[var5];
            //通过bean名称拿到urls
            String[] urls = this.determineUrlsForHandler(beanName);
            if (!ObjectUtils.isEmpty(urls)) {
            //注册进去拦截器
                this.registerHandler(urls, beanName);
            }
        }
        if (this.logger.isDebugEnabled() && !this.getHandlerMap().isEmpty() || this.logger.isTraceEnabled()) {
            this.logger.debug("Detected " + this.getHandlerMap().size() + " mappings in " + this.formatMappingName());
        }
    }
    protected void initApplicationContext() throws BeansException {
        this.extendInterceptors(this.interceptors);
        //探索拦截器MappedInterceptor.class
        this.detectMappedInterceptors(this.adaptedInterceptors);
        //初始化普通拦截器 
        this.initInterceptors();
    }
    protected void extendInterceptors(List<Object> interceptors) {
    }
    protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
        mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), MappedInterceptor.class, true, false).values());
    }
    protected void initInterceptors() {
        if (!this.interceptors.isEmpty()) {
            for(int i = 0; i < this.interceptors.size(); ++i) {
                Object interceptor = this.interceptors.get(i);
                if (interceptor == null) {
                    throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
                }
                this.adaptedInterceptors.add(this.adaptInterceptor(interceptor));
            }
        }

    }

这里我们可能比较疑惑,这里的interceptors是怎么来的,还记得我们项目中需要注册一下拦截器的操作么。到这里我们知道拦截器的初始化过程。那么我们的控制器是如何初始化的?

通过观察,我们大概猜测在initapplicationContext之后的registerHanders方法实现的。

那么HandlerAdapter是做什么的。根据字面意思应该是适配器的意思。通过对不同的实现查看,发现HandlerAdapter对不同传入的handler可以返回不同的值。也就是说这里是对控制器进行判断的,比如对不同的请求来判断是否具有这个这个控制器的执行权利。

就目前来说,只能这样看待上述分析,有些问题现在还决定不了。只能通过调用链来逐步对之前的想法进行二次确认。