共计 1636 个字符,预计需要花费 5 分钟才能阅读完成。
当我开始学习 Spring 时,两个“难”的问题次要在我脑海中回旋:
如何创立单例 bean,而后如何在不同的类中主动拆卸单个 bean?
设想一下这种状况:
有 2 个用户,其中一个想要登录,另一个想要同时在咱们的应用程序中创立报告。login 和 createReport 办法都应用范畴为单例的 userService bean。在这种状况下,这些办法是否按程序应用该单例 bean?否则 singleton bean 如何同时解决多个申请?
答复他们并不像我想的那么艰难。只是须要廓清简略但重要的要点。这就是为什么我会尝试用根本的代码示例来形容它们。让咱们开始:
1. 先讲一下 Spring 容器会比拟好。因为我认为这会帮忙你在脑海中更好地形容过程。
Spring 容器在其中创立 bean。创立所需的 bean 后,它会注入它们的依赖项。容器通过读取配置元数据(XML 或 Java 正文)来获取指令。因而,在初始化 Spring 容器后,您的应用程序就能够应用了,如下图所示:
当你像上面这样定义一个 bean 定义时,你通知容器它必须只为容器中的那个 bean 定义创立一个实例:
<bean id=”accountDao”class=”…”scope=”singleton”/>
此单个实例存储在此类单例 bean 的缓存中。而后 Spring 容器将这个缓存的对象返回给具备该 bean 定义的 bean 的所有申请和援用:
如果咱们想用 new() 运算符显示下面的示例,以形容 Spring 容器在应用程序启动时所做的简化视图,咱们能够编写如下代码:
UserService userService = new UserService();
UserController userController = new UserController();
userController.userService = userService;
ReportController reportController = new ReportController();
reportController.userService = userService
复制代码
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping(value = "/login/{username}")
public User login(@PathVariable(value = "username") String username){System.out.println(Thread.currentThread().getName() + "-----------" + username + "-----------" + new Date());
return userService.login(username);
}
}
复制代码
这些线程别离与单例 bean 一起工作。如何?让咱们略微谈谈 Java 中的内存调配。
在 Java 中,每个对象都是在堆中创立的。堆是全局共享内存。这就是为什么每个线程都能够拜访堆中的对象。
然而堆栈仅用于执行一个线程。在那个线程中,当一个办法被调用时,一个新的块会以 LIFO(Last-In-First-Out) 程序在堆栈中创立。该块保留本地原始值和对办法中其余对象的援用。并且栈内存不能被其余线程拜访。
所以当咱们创立单例 bean 时,它驻留在堆中。因为能够从应用程序的任何地位拜访堆,因而每个创立的线程都能够指向该单例 bean。这是怎么产生的?当线程申请单例 bean 时,它会(借助堆栈中的援用变量)援用堆中单例 bean 的字节码。所以多个线程能够同时援用单例 bean。编译器将指向雷同的字节码并简略地执行它并将办法特定值别离存储在堆栈中的相应块中。没有阻止编译器执行此操作的限度。Singleton 类对 JVM 的惟一限度是它只能在堆中领有此类的一个实例。这就是为什么现实的单例 bean 必须是无状态的。否则可能会呈现并发问题。