这一节咱们来答复上篇文章中避而不谈的无关什么是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 三 :基于注解配置
发表回复