前言
前几日遇到了关于spring的一个很是头痛的问题,最后也算是勉强解决了问题,但是,心里仍旧是有一些不爽。
问题背景
在我的上一篇博客里我写到了在实际的开发中我使用webApplicationContext获取bean无法获取其引用,说来奇怪,后来在网上搜索说是spring-mvc的ioc容器只是web容器的ioc容器的一部分,虽然大致知道了原因,可是真的是大致啊!我不满足啊!怕再次被坑到!于是就跑到spring的官网上翻起了文档,这一翻,让我找到了下面的这张图!
当看到这张图的时候,我的心里豁然开朗了,那感觉酸爽!仔细看这幅图我们就可以发现,原来真的存在两个ioc容器,其实也不难理解,我们去看看web项目中常见的xml配置就可以看到如下的配置:
<!-- Spring配置开始 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:META-INF/ApplicationContext.xml
</param-value>
</context-param>
<!-- 服务器启动即加载 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 指定配置文件的位置 -->
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
这上面的话,我们可以看到在web项目启动的时候其实加载了两次spring的配置文件,而这两次其实就对应着上面的两个容器。上面的容器基本上是关于spring-mvc的请求拦截、响应、视图的。而下面则就是我们能够直接操作到的ioc容器。我们如果获取ioc容器,默认也会得到这个容器。而无法引用到servlet对应的容器里面的bean。为了做出区分,我们下面就将这两个容器分为ioc容器和mvc-ioc容器。
解决
我们上面其实说到我们面临的问题是:ioc容器无法无法得到mvc-ioc容器的bean的引用,其实,仔细一想,你就会发现,这种限制简直太合情合理!联想一下我们变量的作用域,大作用域范围内的变量是无法访问小作用域内的变量的,反之确实可以的。这其实是因为,封装,封装告诉我们所要使用的模块对我们来说是透明的,所以你无法得知模块内的具体信息。但是反过来,模块内部是可以看到外面的。
明白了这一点,那我们如果非要获取,我们该怎么办呢?其实道理很简单,那就是让这两个容器合二为一。做起来也是无丝毫压力。那就是把配置文件集中到listener里面加载,servlet不加载配置文件即可!如果你这样做了,你得到的将是下图:
官网上也有一句话告诉我等可以这样做:
It is also possible to have just one root context for single DispatcherServlet scenarios.
新的问题
在我拿着这个问题去请教大神的时候,大神说他也遇到类似的问题,那就是他用aop去切controller的时候,切不到啊切不到!我一想,不就和我这个问题是一样的吗,ioc容器是无法引用ioc-mvc容器中的bean的,当然,也就切不到了!
结论
通过今天的这个问题我们可以get到以下的点:
- spring-mvc的bean容器从属与ioc容器,记着他们的作用域偶!
- 我们可以在配置的时候将其一起加载,这将不会再出现我们遇到的问题!
- 遇到问题多去官网上看看,不要先着急买书,其实好多书就是帮你翻译了官网而已!
最后,晚安!笨媳妇
欢迎大家到我的个人博客,那里有更多的干货!