要了解应用程序范围和单例范围之间的区别,您需要了解 ServletContext 和 ApplicationContext 是什么。
A ServletContext
在同一 servlet 容器(例如 Tomcat)上的所有 servlet 之间共享。这是一个 Java EE 类(它属于包javax.servlet
)。 Bean 注释为@ApplicationScope
绑定到 ServletContext。
An ApplicationContext
代表一个 Spring IoC 容器,因此它是一个 Spring 特定的类(它属于包org.springframework.context
)。单例作用域 bean 绑定到 ApplicationContext。
您可以在同一个 servlet 容器中拥有多个 IoC 容器,因此您可以拥有多个相同类型的单例 bean,但每种类型只能有一个应用程序范围的 bean。
我提供了一个使用 Spring Boot 和 Spring MVC 的示例。
我们还需要介绍一下DispatcherServlet
。 DispatcherServlet 接收 HTTP 请求并将它们转发到适当的控制器。 ApplicationContext 与每个 DispatcherServlet 相关联。可以将 Spring 配置为创建多个 DispatcherServlet 并将不同的 ApplicationContext 关联到每个 DispatcherServlet。
请注意,在标准配置中只有一个 DispatcherServlet,因此通常无法区分单例作用域 bean 和应用程序作用域 bean。我还想指出,除了提供这两个范围之间差异的具体示例之外,我认为我将向您展示的自定义配置没有实际用途。
下图显示了示例中所有元素之间的关系:
让我们看一下代码。请注意包名!
包装内com.example.demo.beans
我们创建了 2 个 bean。 beans初始化时会生成一个随机数,以便我们区分它们。
@ApplicationScope
@Component
public class MyApplicationScopeBean {
private final double id = Math.random();
public double getId() {
return id;
}
}
// Singleton is the default scope
@Component
public class MySingletonBean {
private final double id = Math.random();
public double getId() {
return id;
}
}
启动类放入com.example.demo
:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// The first ApplicationContext is instantiated here
// (it is also returned by the method).
SpringApplication.run(DemoApplication.class, args);
}
/**
* A new DispatcherServlet is instantiated and registered.
*/
@Bean
public ServletRegistrationBean mvc2() {
// A new ApplicationContext is created, using a dedicated Configuration class.
AnnotationConfigWebApplicationContext secondApplicationContext = new AnnotationConfigWebApplicationContext();
secondApplicationContext.register(Mvc2Config.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setApplicationContext(secondApplicationContext);
DispatcherServletRegistrationBean servletRegistrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/second/*");
servletRegistrationBean.setName("second");
return servletRegistrationBean;
}
}
在同一个包中,我们还放置了将绑定到 Spring Boot 生成的默认 DispatcherServlet 的控制器:
@RestController
public class FirstController {
@Autowired
private MyApplicationScopeBean applicationScopeBean;
@Autowired
private MySingletonBean singletonBean;
@GetMapping("/first/demo")
public String output() {
return "applicationScope=" + applicationScopeBean.getId() + ", singleton=" + singletonBean.getId();
}
}
包装内com.example.demo.mvc2
我们放置第二个配置类:
@Configuration
@ComponentScan(basePackages = {"com.example.demo.mvc2", "com.example.demo.beans"})
@EnableWebMvc
public class Mvc2Config {
}
控制器绑定到第二个 DispatcherServlet:
@RestController
public class SecondController {
@Autowired
private MyApplicationScopeBean applicationScopeBean;
@Autowired
private MySingletonBean singletonBean;
@GetMapping("/demo")
public String output() {
return "applicationScope=" + applicationScopeBean.getId() + ", singleton=" + singletonBean.getId();
}
}
如果运行代码,您可以看到应用程序作用域 bean 的 id 在两个控制器中相同,而单例 bean 的 id 发生了变化:
http://localhost:8080/first/demo
applicationScope=0.8685117272969953,单例=0.23475401462261436
http://localhost:8080/second/demo
applicationScope=0.8685117272969953,单例=0.8390865330171554