Spring 总结
Spring 如何加载注解
很多注解本身只是提供了一个标识,要实现注解所表示的功能,必然还会有一个扫描器扫描这个注解,然后将必须的Bean注入到Spring容器内,而且很多时候会为被注解的对象生成一个动态代理,以实现日志记录、接口幂等、限流等功能。
要自己实现一个注解,关键是如何扫描及如何生成代理并注入到Spring容器这两个步骤,具体的实现可以参考MapperScannerConfigurer
,大体逻辑是:
- 在Spring容器加载完毕后,再对指定包下的类进行一次扫描;
Spring 三级缓存
Spring 中产生循环依赖的三种情况
- 构造器注入循环依赖 构造器注入构成的循环依赖,此种循环依赖方式是无法解决的,只能抛出 BeanCurrentlyInCreationException 异常表示循环依赖。
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) {
}
}
不能解决的原因是:Spring 解决循环依赖的原理是实例化 Bean 后先把引用存到一个 Map 中,之后初始化成员变量时,可以直接从这个 Map 中取。但是构造器注入相当于实例化和初始化是同时进行的,因此无法解决。 - 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;
} - 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 的流程
- createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
- populateBean:填充属性,这一步主要是对 bean 的依赖属性进行注入(@Autowired)
- initializeBean:回到一些形如 initMethod、InitializingBean 等方法
其中,循环依赖可能发生在第一步和第二步,其中第一步是因为构造方法中可能会需要传入其他 Bean。
Spring 三级缓存如何解决循环依赖
缓存生效时间
1 | public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { |
获取单例 Bean 的过程
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)
- 先从一级缓存 singletonObjects 中去获取,如果获取到就直接 return。
- 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存 earlySingletonObjects 中获取,如果获取到就直接 return。
- 如果还是获取不到,且允许 singletonFactories(allowEarlyReference=true)通过 getObject()获取。就从三级缓存 singletonFactory.getObject()获取,如果获取到了就从 singletonFactories 中移除,并且放进 earlySingletonObjects,其实也就是从三级缓存移动到了二级缓存。