目录
- 1. bean注入方式(IOC)
- 2. 有状态会话bean和无状态会话bean
- 3. 单例模式和多例模式
- 4. 深挖多例模式应用场景?
1. bean注入方式(IOC)
2. 有状态会话bean和无状态会话bean
有状态会话bean
:每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。
无状态会话bean
:bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。
面试题:单例 Bean 存在线程安全问题吗?
答:在 Bean 中尽量避免定义可变的成员变量。推荐的做法时在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中。
不过,大部分 Bean 实际都是无状态(没有实例变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。
3. 单例模式和多例模式
@Scope注解主要作用是调节Ioc容器中的作用域,在Spring IoC容器中主要有以下几种作用域:
基本作用域:singleton(单例)、prototype(多例);
Web 作用域(reqeust、session、globalsession);
自定义作用域。
目前有90%以上的业务系统都使用singleton单实例
,因此spring也默认的类型也是singleton,singleton保证了全局是一个实例,对性能有所提高,但是如果实例中有非静态变量时,可能会导致线程安全、共享资源的竞争等问题。
当设置为prototype(多实例)时:每次连接请求,都会重新生成一个新的bean实例,这也会导致一个问题,当请求数越多,性能会降低,因为频繁创建的新的实例,会导致GC频繁,GC回收时长增加。要根据实际情况选择哪一种方式。
4. 深挖多例模式应用场景?
在Spring中,prototype原型模式(多例模式)使用的场景不多,只有当你的bean的属性中会有数据存在的时候,才需要使用原型模式,否则数据就串了。我自己使用过的一个场景是用EasyExcel实现导入功能的时候会使用到。
在使用EasyExcel读取表格文件时,官方文档推荐的是自己进行new一个Listener对象,但是想在这个对象中使用其他service对象的话,就不能使用@Autowired了,因为这个Listener对象并不受容器管理。其中Listener对象中用到的其他service或者DAO类通过构造方法初始化进去,如果直接用@Autowired注入会报空指针错误,项目中代码如下:
Controller代码:
Listener代码:
我们可以考虑将这个Listener对象变成多例模式,加上@Component和@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)两个注解:
然后在Controller里面通过Spring容器获取这个对象(注意:一定要通过容器手动获取,prototype原型模式的bean如果还通过@Autowired的形式注入的话,这个对象还是同一个,因为@Autowired只会注入一次)
调用方通过Spring注入的方式自动注入EmployeeHouseImportListener,而非new的方式,这样获取到的EmployeeHouseImportListener是被Spring容器管理的,进而可以在EmployeeHouseImportListener中通过@Autowired的方式成功获取到同样被Spring容器管理的UserService,因为UserService 的实现类中有"@Service"注解标记。
//调用方
EmployeeHouseImportListener importListener = SpringUtils.getBean("employeeHouseImportListener");