这一节咱们来答复上篇文章中避而不谈的无关什么是 RootApplicationContext 的问题。
这就须要引入 Spring MVC 的无关 Context Hierarchy 的问题。Context Hierarchy 意思就是 Context 层级,既然说到 Context 层级,阐明在 Spring MVC 我的项目中,可能存在不止一个 Context。
Context 是上下文的意思,咱们能够间接了解为容器,上一篇文章咱们提到了 ServletApplicationContext 的概念,能够了解为 Servlet 容器。
除此之外,Spring 我的项目中必定还有有 IoC 容器,咱们明天就来聊一下这两个容器之间的关系。
为什么须要容器
咱们能够这样了解:但凡具备生命周期的对象,也就是说对象并不是在以后现成创立、应用实现之后就销毁的,而是在以后现成应用实现之后不销毁、其余线程还须要持续应用的。这中对象就须要一个容器来保留。
比方 Spring 的单例 Bean,在 Spring 初始化的过程中就会创立并存入 Ioc 容器,之后利用应用过程中从 Ioc 容器中获取。
Servlet 对象(比方 DispatchServlet)也是这样,在 Web 利用初始化的过程中创立,Controller、ViewResolver、ExceptionHandler 等对象随之也实现创立,这些对象在整个利用的生命周期中会重复应用,因而,Servlet 也必须要有一个容器来存储。
Spring 把容器称之为上下文,Context。
咱们能够把 Servlet 容器叫做 WebApplicationContext,因为 Servlet 的呈现就是为了解决 Web 利用的,所以自然而然的,咱们能够把 Servlet 容器称为 WebApplicationContext。
绝对应的,Spring Ioc 容器,咱们能够称之为 RootApplicationContext:根容器。
Servlet 和根容器的关系
下图高深莫测的阐明了两者之间的关系:
Servlet 容器寄存 Controller、VIewResolver、HanderMapping 等 DispatcherServlet 的相干对象,根容器能够寄存其余 Service、Repositories 等对象。
一个 DispatcherServlet 能够对应的有一个 Servlet 容器,一个 Web 利用能够有多个 DispatcherServlet(这种利用其实比拟少见),所以一个利用能够有多个 Servlet 容器。然而个别状况下,即便有多个 Servlet 容器,一个利用也心愿只有一个根容器,以便在不同的 Servlet 容器之间共享根容器的对象。
举例
咱们上面用几个例子来阐明两者之间的关系。
还是延用上一篇文章的例子,并做如下简略的革新。
首先,减少一个单例 bean,以便启用 Spring IoC 容器。咱们只是简略引入 IoC 容器,单例 bean 不须要太简单,
package org.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class UserService {
@Autowired
ApplicationContext app;
public String getUserInfo(){System.out.println("application in UserService:"+ app);
return "This is userinfo...from UserService...";
}
}
只有一个办法,返回 String。不过为了能阐明以后单例 Bean 所处的容器,咱们通过 @Autowired 引入 ApplicationContext 对象(心愿大家还记得这一点,咱们后面讲过通过什么样的形式,可能在利用中拿到 Bean 所处的 Application 对象),这样的话咱们就可能晓得以后 Spring 利用的 Ioc 容器具体是哪个对象。
其次,咱们须要新增一个 Spring Ioc 容器的配置类,咱们称之为 RootConfiguration,配置类仅指定扫描门路即可:
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("org.example.service")
public class RootConfiguration {}
最初,Controller 革新一下,与 UserService 一样,引入 ApplicationContext(Controller 中的 ApplicationContext,咱们能够人为他就是 Servlet 容器),log 打印一下具体的 Context,同时咱们打印一下以后 Servlet 容器的父容器:
package org.example.controller;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HelloWorldController {
@Autowired
UserService userService;
@Autowired
ApplicationContext app;
@GetMapping("/hello")
@ResponseBody
public String hello(ModelAndView model){
DispatcherServlet d;
String userInfo = userService.getUserInfo();
System.out.println("app in controller:"+app);
System.out.println("servletContext's parent Context"+app.getParent());
return "<h1>"+userInfo+"</h1>";
}
}
OK,筹备工作实现,开始验证。
举例 1 根容器不是必须存在
首先,根容器不是必须存在的。
然而因为咱们减少了 UserService 这个 bean,所以必须有 Ioc 容器,咱们必须为 IoC 容器指定配置文件的包扫描门路。
既然咱们说根容器不是必须存在,那么,意思就是说,Servlet 容器要同时充当 IoC 容器的角色。
所以咱们必须在 MvcConfiguration 中减少宝扫描门路:
package org.example.configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan({"org.example.service","org.example.controller"})
//@ComponentScan({"org.example.controller"})
public class MvcConfiguration {}
MvcInitializer 中的 getRootConfigClasses() 返回 null,则利用初始化的过程中就不会创立根容器(通过查看 createRootApplicationContext 办法源码得出的论断):
public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {return null;}
启动利用,测试后果:
阐明利用是能够失常运行的,后盾 log:
application in UserService:WebApplicationContext for namespace 'dispatcher-servlet', started on Tue Aug 22 22:35:34 CST 2023
app in controller:WebApplicationContext for namespace 'dispatcher-servlet', started on Tue Aug 22 22:35:34 CST 2023
servletContext's parent Contextnull
后盾 log 阐明,Servlet 容器和 Spring IoC 容器是同一个,他们的父容器是 null。
指定 Ioc 容器为父容器
首先批改 MvcConfiguration,指定 Servlet 容器只扫描 Controller 包:
package org.example.configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan({"org.example.controller"})
public class MvcConfiguration {}
MvcInitializer 中的 getRootConfigClasses() 返回咱们新增的 RootConfiguration,则利用初始化的过程中就会创立根容器:
public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {return new Class[] {RootConfiguration.class};
}
启动利用,测试:
application in UserService:Root WebApplicationContext, started on Tue Aug 22 22:44:08 CST 2023
app in controller:WebApplicationContext for namespace 'dispatcher-servlet', started on Tue Aug 22 22:44:09 CST 2023, parent: Root WebApplicationContext
servletContext's parent ContextRoot WebApplicationContext, started on Tue Aug 22 22:44:08 CST 2023
有测试后果可知:
1. UserService 所处的是根容器。
2. Controller 所处的是 Servlet 容器。
3. Servlet 容器的父容器是根容器。
测试后果验证了咱们后面的推论,而且,Spring 底层主动将 Servlet 容器的父容器设置为了根容器!在什么中央设置的?
Spring MVC 框架在 DispatcherServlet 的初始化过程中(init 办法),initWebApplicationContext 的时候设置 ServletContext 的父容器为根容器。
上一篇 Spring MVC 三:基于注解配置