Spring 总结

Spring 如何加载注解

很多注解本身只是提供了一个标识,要实现注解所表示的功能,必然还会有一个扫描器扫描这个注解,然后将必须的Bean注入到Spring容器内,而且很多时候会为被注解的对象生成一个动态代理,以实现日志记录、接口幂等、限流等功能。
要自己实现一个注解,关键是如何扫描及如何生成代理并注入到Spring容器这两个步骤,具体的实现可以参考MapperScannerConfigurer,大体逻辑是:

  1. 在Spring容器加载完毕后,再对指定包下的类进行一次扫描;

Spring 三级缓存

Spring 中产生循环依赖的三种情况

  1. 构造器注入循环依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Service
    public class A {
    public A(B b) {
    }
    }
    @Service
    public class B {
    public B(A a) {
    }
    }
    构造器注入构成的循环依赖,此种循环依赖方式是无法解决的,只能抛出 BeanCurrentlyInCreationException 异常表示循环依赖。
    不能解决的原因是:Spring 解决循环依赖的原理是实例化 Bean 后先把引用存到一个 Map 中,之后初始化成员变量时,可以直接从这个 Map 中取。但是构造器注入相当于实例化和初始化是同时进行的,因此无法解决。
  2. singleton 模式 field 属性注入循环依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Service
    public class A {
    @Autowired
    private B b;
    }

    @Service
    public class B {
    @Autowired
    private A a;
    }
  3. prototype 模式 field 属性注入循环依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Service
    public class A {
    @Autowired
    private B b;
    }

    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Service
    public class B {
    @Autowired
    private A a;
    }

Spring 创建 Bean 的流程

  1. createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
  2. populateBean:填充属性,这一步主要是对 bean 的依赖属性进行注入(@Autowired)
  3. initializeBean:回到一些形如 initMethod、InitializingBean 等方法

其中,循环依赖可能发生在第一步和第二步,其中第一步是因为构造方法中可能会需要传入其他 Bean。

Spring 三级缓存如何解决循环依赖

缓存生效时间

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

// 用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

// 提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

// 单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

获取单例 Bean 的过程

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)

  1. 先从一级缓存 singletonObjects 中去获取,如果获取到就直接 return。
  2. 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存 earlySingletonObjects 中获取,如果获取到就直接 return。
  3. 如果还是获取不到,且允许 singletonFactories(allowEarlyReference=true)通过 getObject()获取。就从三级缓存 singletonFactory.getObject()获取,如果获取到了就从 singletonFactories 中移除,并且放进 earlySingletonObjects,其实也就是从三级缓存移动到了二级缓存。