bean 的作用域
-
单例(Singletion) : Spring 容器中只有一个 bean ,这个 bean 在整个应用程序内共享。
-
原话(Prototype) : 每次 getBean(), 都是不同的bean,都会创建一个实例。
-
请求(Request):每个HTTP请求都会创建一个新的 Bean 实例。
-
会话(Session) :每个用户会话会创建一个新的 Bean.
-
应用程序(Application) : 每个应用程序会创建一个新的 Bean
-
自定义作用域 @Scope(value = "目标的作用域" , proxyMode = ScopeProxyMode.TARGET.CLASS)
Bean 的生命周期
对应的完整步骤:
实例化
首先通过 createBeanInstanse 创建实例对象,这里使用反射机制利用 BeanClass 拿到类的构造方法,如果有多个构造方法,就拿有@Autowired 注解的构造方法,如果有两个有@Autowired 注解的构造方法,那么就会报错。
选择好构造方法后,就要确定参数,这里会在体内的单例池内进行查找,如果找到了,那么通过反射就可以开始实例化对象了。如果没有找到,那么会报错,信息不完整。
如果选择的是无参构造方法,那么直接构造。
属性填充
通过三级缓存进行依赖注入。
初始化
利用 initializeBean 方法进行初始化
具体步骤
-
填充初始化容器相关信息
通过 invokeAwareMethods 方法,为实现 aware 接口(信息感知接口)的Bean 注入 beanName,beanFactory 等容器信息。
-
初始化构造方法 invokeInitMethods
通过重写 InitializingBean 接口而实现 afterPropertiesSet 方法来实现的。afterPropertiesSet 写在填充属性后。如果自定义了 initMethod 方法,还会执行这些自定义构造方法。
-
Bean 的后置处理
在 invokeInitMethods 前后进行
-
applyBeanPostProcessorsBeforeInitialization
-
invokeInitMethods
-
applyBeanPostProcessorsAfterInitialization
-
使用
将产生的对象放入单例池中。
销毁
在销毁前需要要执行销毁前处理器,这样就会执行 @PreDestroy注解的方法。
然后通过 destroyBeans 方法逐一摧毁体内所有 bean
然后还会执行用户自定义的销毁方法,但是需要手动指定。
单例 Bean 的线程安全问题
单例 Bean 是线程不安全的,如果只对 bean 执行查询的操作,不对bean 进行修改,那么这个单例 Bean 是线程安全的。
解决办法
-
将单例 bean 定义为多例 Prototype,不方便管理。
-
在 Bean 对象中,尽量避免定义可变的成员变量,但是这样会限制很多,所以不能这样做。
-
利用 ThreadLocal,将 Bean 中的成员变量保存在 ThreadLocal 中。
ThreadLocal 可以保证变量在多线程环境下,保证变量隔离。
public class MyClass { private ThreadLocal<String> threadLocal = new ThreadLocal<>(); public void setThreadLocal(String value) { threadLocal.set(value); } public String getThreadLocal() { return threadLocal.get(); } }
可以在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 里,这是推荐的一种方式。