热门框架系列 -- SpringMvc的父子容器,SpringBoot是否有父子容器?
Posted 喜欢编码的老胡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了热门框架系列 -- SpringMvc的父子容器,SpringBoot是否有父子容器?相关的知识,希望对你有一定的参考价值。
@TOC# 热门框架系列
记录在程序走的每一步___auth:huf
从新的篇章开始;篇章阅读需要先关注; 因为笔者想参与技术文章的评选.;需要一定的粉丝量; 粉丝量达到一定数量.所有文章阅读限制将会全面放开;谢谢大家的支持
本章节为上一个章节的延续;仅此一章节的延续; 先抛出一个问题;请记住这个问题;之后看完这个篇章应该可以轻松的回答出来;
SpringBoot 既然整合了SpringMVC 那SpringBoot 有没有子容器?
我们开始这个篇章吧!
在Servlet3.X,我们可以添加 Servlet Filter Listener
我们有两种方式 一种是注解的方式;
@WebServlet @WebFilter @WebListener
还有一种方式 就是 :Spi
spi 是什么?
SPI 我们叫他服务接口扩展,(Service Provider Interface) 直译服务提供商接口, 不 要被这个名字唬到了, 其实很好理解的一个东西: 其实就是根据Servlet厂商(服务提供商)提供要求的一个接口, 在固定的目录 (META-INF/services)放上以接口全类名 为命名的文件, 文件中放入接口的实现的 全类名,该类由我们自己实现,按照这种约定的方式(即SPI规范),服务提供商会 调用文件中实现类的方法, 从而完成扩展。
在其规范中 我们可以看得到那么一句话:
也就是META-INF/services 路径下 放一个 javax.servlet.ServletContainerInitailizer
以下我们进入SPI 代码的实现:
准备一个maven 项目 里面三个子项目 一个Service 一个Client; 还有一个startUp 启动 我们按照上面的说法 我们在 META-INF/service 路径下放一个 接口全路径名的File 文件 文件内维护全路径类名的实现类
以下是测试结果:
SPI 机制就是这样;
现在很多第三方插件使用的都是SPI机制; 当然Spring也有这样的机制;
前置知识点 到这里 就铺垫的差不多了; 如果SPI又疑问的;可以单独私聊我;
我们细细的往下挖,IDEA创建spring mvc项目 如何创建 可以在其他博主内找到; 以下是需要注意的几点:
1: 需要配置Tomcat;
Tomcat下载下来 启动会有乱码 以下是windows解决乱码的方案:
在Tomcat的Config里面 logging.properties
修改:
java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.encoding = GBK
2:需要配置dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<context:component-scan base-package="com.huf"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
3:需要配置web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
如果对于这部分代码配置 或者使用idea 搭建 SpringMVC搭建 项目 过程中 遇到有什么问题 可以私聊我; 因为如果想要追溯 父子容器 那就必须要搭建这么一套东西;
我们在项目中的META-INF.services 添加 javax.servlet.ServletContainerInitializer
SpringMVC 我们分为2块来看 第一块 启动:
我们现在了解了SPI 也配置好了Tomcat 启动这一块 我暂时不细说; 后面有专门的文章来说Tomcat;
1:在我们的SPI中 我们有一个类放在META-INF下面:HufSpringServletContainerInitializer
之后
我们进入到了AbstractContextLoaderInitializer.onStartup 方法;
继承自AbstractAnnotationConfigDispatcherServletInitializer
以下是他们的继承关系
以上的铺垫 全部都是为了引出 加载容器方法; 我们联合起来的逻辑:
我们知道 在子类没有onStartup方法 就会往父类中去寻找:
我们再AbstractDispatcherServletInitializer.onStartup方法中;我们可以发现;
这里 就准备开始创建我们的父容器; 点进去;
开始创建我们的父容器;
继续往里面看
开始注册我们的父容器; 只有register 没有refresh(); 这里我们就可以发现 这里仅仅是做了一个准备工作 容器并没有启动;最后返回一个容器;
创建一个监听器;
新创建一个监听器 然后把监听器 方到Listener里面;父容器到这里就阶段性结束了;然后回到 AbstractDispatcherServletInitializer.onStartup()方法中
代码部分 我就不一句一句去解释了 这里代码非常简单;如果喜欢看我博客的同学 应该都没很大问题;
这里记住 Tomcat会继续往下执行代码; 这时候会调起一个 ContextLoaderListener的contextInitialized 方法;
我们继续点进去看一下;
tomcat会继续往下执行 会调起 DispatcherServlet.init 方法 这里再上一篇文章中我写有写一部分;
重点关注这个方法;
protected WebApplicationContext initWebApplicationContext()
从域中提取出父容器;
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null)
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext)
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive())
if (cwac.getParent() == null)
设置父容器
cwac.setParent(rootContext);
开始初始化容器; 再上图中 进行 refresh
configureAndRefreshWebApplicationContext(cwac);
if (wac == null)
wac = findWebApplicationContext();
if (wac == null)
wac = createWebApplicationContext(rootContext);
if (!this.refreshEventReceived)
synchronized (this.onRefreshMonitor)
开始初始化Servlet里面的 组件
/**
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
**/
onRefresh(wac);
if (this.publishContext)
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
return wac;
这边 我们就已经把 SpringMVC的父子容器全部解释清楚了:
总结
1:Tomcat在启动时会通过SPI注册 ContextLoaderListener和DispatcherServlet对象,同时创建父子容器 :分别创建在ContextLoaderListener初始化时创建父 容器设置配置类;在DispatcherServlet初始化时创建子容器 即2个 ApplicationContext实例设置配置类
2: Tomcat在启动时执行ContextLoaderListener和DispatcherServlet对象的初始化方 法, 执行容器refresh进行加载
3:在子容器加载时 创建SpringMVC所需的Bean和预准备的数据;
4: 子容器需要注入父容器的Bean时(比如Controller中需要@Autowired Service的Bean); 会先从子容器中找,没找到会去父容器中找: 详情见 AbstractBeanFactory#doGetBean方法
一般情况下,只有Spring和SpringMvc整合的时才会有父子容器的概念,
作用:比如我们的Controller中注入Service的时候,
发现我们依赖的是一个引用对象,
那么他就 会调用getBean去把service找出来;
但是当前所在的容器是web子容器,
那么就会在这里的 先去父容器找
回到我们 文章当中的那个问题: