1.Spring 中的bean的作用域有哪些?
- Singleton: 唯一bean实例,Spring中的bean默认都是单例的。
- prototype:每次请求都会创建一个新的bean实例。
- request:每一次HTTP请求都会产生一个新的bean,该bean仅在HTTP request内有效。
- session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。
- global-session:全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(例如HTEML)片段的小型Java Web插件。他们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与servlet不同,每个portlet都有不同的会话。
2.Spring中的单例bean的线程安全问题了解吗
单例bean存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象非静态成员变量的写操作会存在线程安全问题。
常见的解决方法:
- 在Bean对象中尽量避免定义可变的成员变量
- 在类中定义ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中
3.@Component 和@Bean的区别是什么?
- 作用对象不同:@Component注解作用于类,而@Bean注解作用于方法。
- 针对一些第三类库的话,我们并不知道它的源码,也就无法直接使用注解@Component,我们可以采用@Configuration+@Bean的注解,将它注册到IOC容器中。
4.将一个类声明为Spring 的bean的注解有哪些
- Autowired//默认按type注入,然后按名称
- Qualifier(“cusInfoService”)//一般作为@Autowired()的修饰用
- Resource(name=“cusInfoService”)//默认按name注入,可以通过name和type属性进行选择性注入
并且Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。
采用以下注解可实现:
- @Component:通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪个层,可用使用@Component注解标注。
- @Repository:对应持久层即DAO层,主要用于数据库相关操作。
- @Service:对应服务层,主要涉及一些复杂的逻辑,需要用到DAO层。
- @Controller:对应Spring MVC控制层,主要用户接受用户请求调用Service层返回数据给前端页面。
5.Spring Bean的生命周期
- 实例化Instantiation
- 属性赋值Populate
- 初始化Initialization
- 销毁Destruction
- 实例化:第一步,实例化一个bean对象
- 属性赋值:第二步,为bean设置相关属性和依赖
- 初始化:第3-7步,
3.检查Aware的相关接口并设置相关依赖
4.BeanPostProcessor前置处理
3-4在初始化前进行
5.是否实现InitializingBean接口
6.是否配置自定义init-method
5-6为初始化操作
7.BeanPostProcesspor后置处理 - 销毁:
8.注册Destruction相关回调接口
第8步还未真正销毁。
9.是否实现DisposableBean接口
10.是否配置自定义的destory-method
Bean的实例化和BeanWrapper
容器在内部实现的时候,采用策略模式来决定以何种方式来实例化bean实例。
默认情况下,容器内部采用的是CglibSubclassingInstantiationsStrategy
容器根据bean的定义的BeanDefintion取得实例化信息,结合CglibSubclassingInstantiationStrategy以及不同的bean定义类型,就可以返回实例化完成的对象实例。
但是不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。
BeanWrapper接口通常在Spring框架内部使用,它有一个实现类org.springframework.BeanWrapperImpl。其作用是对某个bean进行包裹,然后对这个包裹的bean进行操作,比如设置或者获取bean的相应属性值。而在第一步结束返回BeanWrapper实例而不是原先的对象实例,就是为了第二步设置对象属性。
使用BeanWrapper对bean实例操作很方便,可以免去直接使用Java反射API操作对象实例的繁琐。
各色的Aware接口
当对象实例化完成并且相关属性以及依赖设置完成之后,Spring容器会检查当前对象实例是否实现了一系列的以Aware命名结尾的接口定义。如果是,则将这些Aware接口定义中规定的依赖注入给当前实例:
- BeanNameAware:将对象实例的bean定义对应的beanName设置到当前对象实例。
- BeanClassLoaderAware:将对应加载当前bean的ClassLoader注入当前对象实例。
- BeanFactoryAware:注入当前BeanFactory容器的引用。
以上几个Aware接口只是针对BeanFactory类型的容器而言的。
对于ApplicationContext类型的容器,也存在几个Aware相关接口。使用的是下面要说的BeanPostProcessor方式。
BeanPostProcessor
BeanPostProcessor会处理容器内所有符合条件的实例化后的对象实例。
常用的场景:
- 对于标记接口的实现类,进行自定义处理。
- 为当前对象提供代理实现,生成对象的代理类,然后返回。
总结:
- 首先是实例化、属性赋值、初始化、销毁这4个大阶段
- 再是初始化的具体操作,有Aware接口的依赖注入、BeanPostProcessor在初始化前后的处理已经IntializingBean和init-method的初始化操作。
- 销毁的具体操作,有注册相关销毁回调接口,最后通过DisposableBean和destory-method进行销毁。