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可以返回不同的值。也就是说这里是对控制器进行判断的,比如对不同的请求来判断是否具有这个这个控制器的执行权利。
就目前来说,只能这样看待上述分析,有些问题现在还决定不了。只能通过调用链来逐步对之前的想法进行二次确认。
- Jrebel6.3.3破解,配置图文教程
- Spring Cloud(十一)高可用的分布式配置中心 Spring Cloud Bus 消息总线集成(RabbitMQ)
- Keras中带LSTM的多变量时间序列预测
- Spring Cloud(十)高可用的分布式配置中心 Spring Cloud Config 中使用 Refresh
- Hibernate 的性能优化的时候碰到了"抓取策略",有四种
- 基于 Spring Cloud 完整的微服务架构实战
- maven build时报错Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
- Spring Cloud(九)高可用的分布式配置中心 Spring Cloud Config 集成 Eureka 服务
- Spring Cloud(八)高可用的分布式配置中心 Spring Cloud Config
- 用Raspberry Pi Zero打造「即插即用」的Web服务器
- Spring Cloud(七)服务网关 Zuul Filter 使用
- 基于Metronic的Bootstrap开发框架经验总结(1)-框架总览及菜单模块的处理
- Spring Cloud(六)服务网关 zuul 快速入门
- Docker Registry Server 搭建,配置免费HTTPS证书,及拥有权限认证、TLS 的私有仓库
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 使用ABAP实现Mock测试工具Mockito
- 增强版本的自开发SAP WebClient UI Repository Information System
- 最大子序列和的问题的解(1)
- 10-STM32+ESP8266+AIR202远程升级方案-功能3-手机APP控制STM32远程更新固件程序,基于ESP8266
- 最大子序列和的接口函数(2)
- 最大子序列和的接口函数(3)
- 【剑指Offer】二叉树的深度
- 运行时间中的对数
- IIC协议
- 通过例子学习编写shell
- 【redis6.0.6】redis源码慢慢学,慢慢看 -- 第三天:MakeFile
- 继续学习Shell脚本(详细)
- 将linux终端的输出信息保存到log中
- UNIX网络编程卷1(第三版)一个简单的时间获取服务器的程序
- Python数据分析实战(3)Python实现数据可视化