SpringBean和IOC容器

IOC

  • Java对象:开发者自己创建对象、管理对象,使用时自行管理依赖的注入,比如自行实现单例模式、工厂方法来创建特定的对象;
  • SpringBean:由SpringIOC容器统一创建、管理、销毁的Java对象;开发者定义好Bean的配置信息、依赖,由Spring容器在启动时完成Bean的初始化和实例化,并且Bean的整个生命周期由IOC管理,IOC容器创建Bean的过程中会执行一系列回调方法,可由开发者干预和定制;

IOC容器的核心思想是:工厂模式 + 反射机制;

工作方式:Spring在启动时创建容器(本质是ConcurrentHashMap)来存储所有的Bean,根据开发者的注解或XML配置加载Java对象,并向这些Bean注入配置好的依赖属性,减少开发者自己创建对象、维护对象间依赖关系。

SpringBean的作用域

单例(Singleton)、原型(Prototype)、请求(Request)、会话(Session)等;

最常见的就是单例;那么使用单例Bean的注意事项:

  1. 保证Bean是无状态的:不存在可变状态的成员变量;一定要存储数据,使用局部变量或线程本地变量;
  2. 使用方法传递请求对象,而不是存储在成员变量中;
  3. 避免使用非线程安全的成员对象;比如
    SimpleDateFormat

Bean的声明

首先开发者需要向Spring提供Bean的声明;告知要将哪些Bean注入到Spring容器中;一般的方式有:

  • xml声明
  • 注解声明

Spring将这些声明加载并封装为:

BeanDefinition
对象;

此对象定义了Bean的元数据信息、含有的依赖、bean的工程、初始化、销毁方法等等;

private Boolean lazyInit;        // 是否懒加载
private String[] dependsOn;      // 依赖
private volatile Object beanClass; // beanClass对象
private String initMethodName;   // 初始化方法
private String destroyMethodName;// 销毁方法
private String factoryBeanName; // factory

Bean的注入方式

1. 字段注入

public class A {
    @Autowired
    private B b;
}
  • 通过反射直接注入特定的字段;
  • 代码简洁,不需要构造函数、setter方法;但不利于测试,需要支持反射的测试框架或手动设置依赖;
  • 支持自动处理循环依赖(三级缓存)

2. Setter注入

public class A {
    private B b;
    
    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}
  • 从代码层面明确区分了实例化和初始化,可以在Bean创建完成后,执行Setter注入依赖;
  • 封装性被破坏,运行时依赖可以被修改;
  • 支持自动处理循环依赖(三级缓存)

3. 构造器注入

public class A {
    private B b;
    
    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}
  • 强制初始化时,依赖必须存在;
  • 强制不可变,不对外提供setter,Bean创建完成后,依赖则不可变;保证安全;
  • 不支持自动处理循环依赖,若存在循环依赖,强制报错;可以使用这种注入来约束工程中不好的分层习惯;

Bean的循环依赖

循环依赖是一个不应该出现的、系统结构分层不合理的现象;

但是Spring还是在框架层面一定程度上为开发者解决了这个问题,为了实现:

理想设计
现实开发
的一种平衡,Spring本身不鼓励循环依赖,鼓励使用构造器注入依赖;

因此Spring的态度是:

支持循环依赖但不鼓励

解决循环依赖的核心是:分离Bean的实例化和初始化;

  • 完成Bean的创建,让未配置完成的Bean提前暴露出来,可以作为依赖来使用;
  • 使用额外的容器,来存储这些提前暴露出来的Bean、未完成初始化的Bean的工厂;
  • 因此构造器注入不支持循环依赖;

Bean的实例化和初始化

入口:

doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)

  1. Bean的实例化
  2. Bean的提前暴露
  3. Bean的属性填充
  4. Bean的初始化
    1. 前置处理
    2. 初始化
    3. 后置处理

1. 实例化

Spring按照

BeanDefinition
开始创建Bean对象

调用:

createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
方法;

判断当前Bean是否有工厂方法、是否有指定的构造函数等;如果都没有,则使用反射通过无参构造函数创建Bean的空对象;

2. 提前暴露

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));

3. 属性填充

调用:

populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)
完成属性填充;

中间遇到依赖Bean,则填充用户定义的依赖、属性,并扫描Bean实现的Aware接口,回调Aware方法;

如果Bean需要从Spring上下文中获取一些基础设施信息,实现对应的Aware:

  • BeenNameAware:获取Beanname;
  • ApplicationContextAware:如果Bean要获取ApplicationContext对象;
  • BeanClassLoaderAware:获取Bean的类加载器;
  • EnvironmentAware:获取环境配置信息等;

4. 初始化Bean

调用:

initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd)

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
  // 调用bean实现的Aware方法
  invokeAwareMethods(beanName, bean);
  
  // 执行bean的前置处理(如果有)
  wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  
  // 执行初始化方法(如果有)
  invokeInitMethods(beanName, wrappedBean, mbd);

  // 执行bean的后置处理(如果有,SpringAOP在这里执行)
  wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  
  return wrappedBean;
}

4.1 前置处理

执行

BeanPostProcessor
postProcessBeforeInitialization()

PropertyPlaceholderConfigurer
:XML中的占位符,在前置处理中替换为值;

@PostConstruct在前置处理后执行;

4.2 初始化

1、执行实现 InitializingBean 接口的

afterPropertiesSet()
方法(钩子函数)

2、执行用户自定义的

initMethod
初始化方法;

4.3 后置处理

执行

BeanPostProcessor
postProcessAfterInitialization()

  • AnnotationAwareAspectJAutoProxyCreator在此处执行
    wrapIfNecessary
    完成代理对象的创建;