一,作用域定义
限定程序中变量的可用范围叫做作用域,而 Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式,比如 singleton 单例作用域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值
二,同⼀类型多个 @Bean 报错的解决办法
- 方法1:使用正确的bean name 获取
- 方法2:使用@Resource设置name属性
// 注⼊
@Resource(name = "user1")
private User user;
- 方法3:使用@Autowired+@Qualifier
// 注⼊
@Autowired
@Qualifier(value = "user2")
private User user;
三,Bean的6种作用域
Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作用域。Spring有 6 种作用域,最后四种是基于 Spring MVC 生效的:
1.singleton:单例作用域
- 描述:该作用域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过
applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是同⼀
个对象。
- 场景:通常无状态的Bean使用该作用域,无状态表示Bean对象的属性状态不需要更新
- 备注:Spring默认选择该作用域
2.prototype:原型作用域(多例作用域)
- 描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过
- applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的
对象实例。
- 场景:通常有状态的Bean使⽤该作⽤
3.request:请求作用域
- 描述:每次http请求会创建新的Bean实例,类似于prototype
- 场景:⼀次http的请求和响应的共享Bean
备注:限定SpringMVC中使⽤
4.session:回话作用域
- 描述:在⼀个http session中,定义⼀个Bean实例
- 场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
- 备注:限定SpringMVC中使⽤
5.application:全局作用域
- 描述:在⼀个http servlet Context中,定义⼀个Bean实例
- 场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
- 备注:限定SpringMVC中使⽤
6.websocket:HTTP WebSocket 作用域
- 描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
- 场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直到WebSocket结束都是同⼀Bean。
- 备注:限定Spring WebSocket中使⽤
7,单例作用域(singleton)和全局作用域(application)区别
- singleton 是 Spring Core 的作用域;application 是 Spring Web 中的作用域;
- singleton 作用于 IoC 的容器,而application 作用于 Servlet 容器
四,Spring框架中的单例bean是线程安全的吗
-
不是,Spring框架中的单例bean不是线程安全的。
- spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
实际上大部分时候 spring bean 无状态的(比如 dao 类),所以某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。
①有状态就是有数据存储功能。
②无状态就是不会保存数据。
五,Spring如何处理线程并发问题
- 在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题
-
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式
-
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal
六,Spring框架中bean的生命周期
- Bean 执行流程(Spring 执行流程):启动 Spring 容器 -> 实例化 Bean(分配内存空间,从⽆到有)-> Bean 注册到 Spring 中(存操作) -> 将 Bean 装配到需要的类中(取操作)
- Bean的生命周期:
1.实例化 Bean(为 Bean 分配内存空间)
2.设置属性(Bean 注⼊和装配)
3.Bean 初始化
实现了各种 Aware 通知的⽅法,如 BeanNameAware、BeanFactoryAware、
ApplicationContextAware 的接⼝⽅法;
执⾏ BeanPostProcessor 初始化前置⽅法;
执⾏ @PostConstruct 初始化⽅法,依赖注⼊操作之后被执⾏;
执⾏⾃⼰指定的 init-method ⽅法(如果有指定的话);
执⾏ BeanPostProcessor 初始化后置⽅法。
4.使用Bean
5.销毁 Bean
销毁容器的各种⽅法,如 @PreDestroy、DisposableBean 接⼝⽅法、destroy-method
-
具体实例:
ean 的⽣命流程看似繁琐,但咱们可以以⽣活中的场景来理解它,⽐如我们现在需要买⼀栋房⼦,那
么我们的流程是这样的:
1.先买房(实例化,从⽆到有);
2.装修(设置属性);
3.买家电,如洗⾐机、冰箱、电视、空调等([各种]初始化);
4.⼊住(使⽤ Bean);
5.卖出去(Bean 销毁)
七,Bean生命周期演示
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class BeanLifeComponent implements BeanNameAware {
@PostConstruct
public void postConstruct() {
System.out.println("执⾏ PostConstruct()");
}
public void init() {
System.out.println("执⾏ BeanLifeComponent init-method");
}
@PreDestroy
public void preDestroy() {
System.out.println("执⾏:preDestroy()");
}
public void setBeanName(String s) {
System.out.println("执⾏了 setBeanName ⽅法:" + s);
}
}
实例化和初始化的区别:
实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可⼈工预和修改;而初始化是给开发者提供的,可以在实例化之后,类加载完成之前进行自定义“事件”处理
八,哪些是重要的bean生命周期方法? 你能重载它们吗
- 有两个重要的bean 生命周期方法,第一个是setup , 它是在容器加载bean的时候被调用。第二个方法是 teardown 它是在容器卸载类的时候被调用。
- bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)
扩展【什么是Spring的内部bean?什么是Spring inner beans】
在Spring框架中,当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean。内部bean可以用setter注入“属性”和构造方法注入“构造参数”的方式来实现,内部bean通常是匿名的,它们的Scope一般是prototype
九,在 Spring中如何注入一个java集合
Spring提供以下几种集合的配置元素:
十,使用@Autowired注解自动装配的过程是怎样的
使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。
在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:
- 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
- 如果查询的结果不止一个,那么@Autowired会根据名称来查找;
- 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
扩展 【自动装配有哪些局限性】
重写:你仍需用 和 配置来定义依赖,意味着总要重写自动装配。
基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。
模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。