SpringBoot 的@Repository 等注解的底层实现原理
1. @Repository注解的类如何存储的
Spring中使用一个IOC容器存储加了 @Component
/@Repository
/@Service
/@Controller
等注解的类,加载IOC容器基本步骤,当 Spring Boot 应用启动时,@SpringBootApplication
中包含的 @ComponentScan
会扫描包及子包下加了这些注解的类。
Spring 会读取每个使用了这些注解类的元数据,创建对应的BeanDefinition
并注册到 BeanDefinitionRegistry
。
2. IOC容器的默认实现
BeanDefinition
是一个接口,如果一个类使用派生自@Component
的注解,比如@Repository
/@Service
/@Controller
等,并且通过@ComponentScan
注解扫描注册到IOC容器时,默认的接口BeanDefinition
实现类是AnnotatedGenericBeanDefinition
。
BeanDefinitionRegistry
也是一个接口,默认的实现是DefaultListableBeanFactory
。
BeanDefinition
中记录的。
3. IOC容器如何管理实例
默认情况下注册到IOC容器中的实例是单例,能保证每次获取到的是同一个实例。
上图是DefaultListableBeanFactory
继承结构图,当使用派生自@Component
注解一个类时, Spring 内部使用一个三层缓存机制来管理Bean的生命周期,核心是在DefaultSingletonBeanRegistry
中维护的。核心源码:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {/*** 一级缓存:存放完全初始化的单例 Bean 实例*/private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 二级缓存、三级缓存用于解决循环依赖,与本文关系不大,省略...
}
4. 从IOC容器中获取实例
IOC容器主要通过getBean()
获取托管的Bean实例,该方法由接口BeanFactory
声明,由AbstractBeanFactory
实现。下面是通过getBean()
方法获取一个 Bean 时的基本执行顺序:
① 是否已经在 singletonObjects
中有现成的 Bean 实例;
② 如果没有并且是懒加载的Bean,就根据对应的 BeanDefinition
实例化一个对象;
③ 实例化之后,放进 singletonObjects
中缓存起来;
④ 之后所有获取请求,都会返回缓存中的同一个对象。
5. IOC容器创建实例时机
**懒加载方式:**上文提到的懒加载时会在getBean时创建实例外的情况,这里不多描述。
**预先实例化单例:**在使用SpringApplication.run()
启动SpringBoot应用时,经过多层调用最终会调用SpringApplication.refresh()
方法,该方法会根据先前创建的BeanDefinition
对象找出scope
为singleton
且非懒加载的 Bean创建实例对象。