springboot启动流程(九)ioc依赖注入
所有文章
https://www.cnblogs.com/lay2017/p/11478237.html
正文
在前面的几篇文章中,我们多次提到这么一个转化过程:
Bean配置 --> BeanDefinition --> Bean对象
Bean的配置可以是xml配置,也可以是java配置。BeanDefinition配置在内存中数据对象,也是Bean的元数据。在springboot启动过程当中,refresh上下文这个步骤将会解析xml配置以及java配置,从而把Bean的配置解析成为BeanDefinition。我们也可以将这个过程简称为Bean的元数据生成。
这里我们需要注意!refresh只是把BeanDefinition注册到BeanFactory中,而不是把Bean注册到BeanFactory中。(这里我们不讨论non-lazy-init=true的情况)
而是在我们调用上下文的getBean的时候才会去根据BeanDefinition生成
@Override
public Object getBean(String name) throws BeansException {
//
return getBeanFactory().getBean(name);
}
上下文的getBean方法把功能实现委托给了BeanFactory,跟进AbstractBeanFactory的getBean方法
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
doGetBean获取Bean的逻辑
跟进doGetBean方法
protected <T> T doGetBean(
final String name,
@Nullable final Class<T> requiredType,
@Nullable final Object[] args,
boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name);
Object bean; // 如果拿到已经注册的单例Bean,直接返回结果
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else { // try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // // 创建单例
if (mbd.isSingleton()) {
// 回调创建
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
//
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
// 每次创建
prototypeInstance = createBean(beanName, mbd, args);
} finally {
//
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
//
}
} catch (BeansException ex) {
//
}
}
// return (T) bean;
}
该方法的逻辑是先去单例的缓存中找,如果找得到直接返回。如果找不到,那么判断是单例还是原型,如果是单例创建并缓存,如果是原型那么每次都创建新的。
getSingleton获取单例
跟进创建单例的时候的getSingleton方法
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// 内置锁控制
synchronized (this.singletonObjects) {
// 双重校验
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// boolean newSingleton = false;
//
try {
// 回调创建Bean
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
//
}
catch (BeanCreationException ex) {
//
} finally {
//
}
if (newSingleton) {
// 添加单例到缓存中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
这里采用双重校验机制控制了单例,如果二次校验的时候发现缓存中没有Bean,那么就会回调创建的方法去创建一个Bean,然后再注册到本地堆缓存当中。
createBean创建Bean
我们先回到调用getSingleton的方法位置,看一下回调方法实什么
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
//
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
创建实现委托给了createBean方法,该方法的实现属于AbstractAutowireCapableBeanFactory,跟进该类的CreateBean方法
@Override
protected Object createBean(
String beanName,
RootBeanDefinition mbd,
@Nullable Object[] args) throws BeanCreationException {
//
RootBeanDefinition mbdToUse = mbd; // try {
// 创建Bean实例
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//
return beanInstance;
} catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
//
} catch (Throwable ex) {
//
}
}
继续跟进doCreateBean
protected Object doCreateBean(
final String beanName,
final RootBeanDefinition mbd,
final @Nullable Object[] args) throws BeanCreationException { // 创建Bean实例对象
BeanWrapper instanceWrapper = null;
//
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//
try {
// 自动注入
populateBean(beanName, mbd, instanceWrapper);
//
} catch (Throwable ex) {
//
}
// return exposedObject;
}
该方法主要包含两个步骤,创建Bean的实例对象和自动注入
createBeanInstance创建实例
跟进createBeanInstance
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 省略 // 默认使用无参数构造方法获取实例
return instantiateBean(beanName, mbd);
}
跟进instantiateBean方法
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy(); protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
// 实例化
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
} else {
// 实例化
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
} catch (Throwable ex) {
//
}
} // 获取实例化策略
protected InstantiationStrategy getInstantiationStrategy() {
return this.instantiationStrategy;
}
默认的实例化策略是CglibSubclassingInstantiationStrategy,它又继承自SimpleInstantiationStrategy,跟进SimpleInstantiationStrategy类的instantiate方法
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// 如果不存在需要被重写的方法,那么就不需要使用cglib重写并覆盖该类
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
//
}
// 通过构造方法实例化
return BeanUtils.instantiateClass(constructorToUse);
} else {
// 需要通过cglib生成
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
到这里,BeanDefinition就被初步创建成为了一个Bean实例对象。
populateBean自动注入
前面我们说到,doCreateBean有两个步骤
1)创建Bean实例对象
2)自动注入
回顾一下doCreateBean的代码
protected Object doCreateBean(
final String beanName,
final RootBeanDefinition mbd,
final @Nullable Object[] args) throws BeanCreationException { // 创建Bean实例对象
BeanWrapper instanceWrapper = null;
//
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//
try {
// 自动注入
populateBean(beanName, mbd, instanceWrapper);
//
} catch (Throwable ex) {
//
}
// return exposedObject;
}
接下来,我们跟进populateBean方法看看当前这个创建好的Bean实例实怎么注入其它Bean的
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// // 获取待注入的property,配置文件中配置的<property>将在这里被处理
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); // 按照名字或者类型获取属性,这里会进行递归
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// 按照名字获取属性
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// 按照类型获取属性
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
} boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// 后置处理器处理@Autowired @Resource等注解
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
// 注入<property>属性
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
我们看到populateBean主要做两件事,获取属性值,然后把属性值给注入到Bean里面去。我们重点关注后置处理器处理@Autowired @Resource注解的逻辑。
AutowiredAnnotationBeanPostProcessor处理@Autowired注入注解
跟进AutowiredAnnotationBeanPostProcessor类的postProcessPropertyValues方法
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { return postProcessProperties(pvs, bean, beanName);
}
跟进postProcessProperties方法
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 获取当前Bean的元数据,将包含@Autowired等注解的标注的待注入元素
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 注入元素
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
后置处理属性值包含两件事,找到当前Bean被@Autowired等注解标注的待注入的元素,然后注入相应的到元素。
跟进findAutowiringMetadata方法
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
// 构造元数据
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
继续跟进buildAutowiringMetadata
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz; do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); ReflectionUtils.doWithLocalFields(targetClass, field -> {
// 找到注解@Autowired
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
//
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
}); // elements.addAll(0, currElements);
// 往父类递归
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements);
}
这里找到注解@Autowired的Field以后包装成Element,然后向父类递归,最后包装成元数据
我们回到postProcessProperties方法以后,再跟进inject注入方法看看
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
throws Throwable { if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
// 反射设置值,这里的取值会对依赖进行递归处理
field.set(target, getResourceToInject(target, requestingBeanName));
} else {
// 省略
}
}
这里主要是对Bean的Field的一个反射来设置值,值的获取将会进行递归处理
CommonAnnotationBeanPostProcessor处理@Resource注入注解
触发后置处理器的逻辑跟AutowiredAnnotationBeanPostProcessor是一样的,我们直接来看看CommonAnnotationBeanPostProcessor的buildAutowiringMetadata方法
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz; do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); ReflectionUtils.doWithLocalFields(targetClass, field -> {
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
//
}
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
//
}
// 如果注解了@Resource
else if (field.isAnnotationPresent(Resource.class)) {
//
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
// 添加element
currElements.add(new ResourceElement(field, field, null));
}
}
}); // elements.addAll(0, currElements);
// 向父类递归
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements);
}
元数据返回以后的流程和@Autowired也是一样的。
总结
本文粗略地过了一下ioc依赖注入的过程,从BeanDefinition --> Bean的过程。我们一开始创建了Bean的实例,然后再通过递归解析依赖注入处理把Bean之间的关系结合处理。在最后还提了一下@Autowired和@Resource的后置处理器。
依赖注入的过程相对来说还是很复杂的,包含了非常多的细节处理。但是我们可以简单地去概括一下它,整个依赖注入的过程就是创建Bean,并建立Bean之间的关系。
springboot启动流程(九)ioc依赖注入的更多相关文章
- SpringBoot启动流程分析(六):IoC容器依赖注入
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(四):IoC容器的初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(一):SpringApplication类初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(二):SpringApplication的run方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程及其原理
Spring Boot.Spring MVC 和 Spring 有什么区别? 分别描述各自的特征: Spring 框架就像一个家族,有众多衍生产品例如 boot.security.jpa等等:但他们的 ...
- SpringBoot启动流程解析
写在前面: 由于该系统是底层系统,以微服务形式对外暴露dubbo服务,所以本流程中SpringBoot不基于jetty或者tomcat等容器启动方式发布服务,而是以执行程序方式启动来发布(参考下图ke ...
- SpringBoot 启动流程
SpringBoot 启动流程 加载 resources/META-INF/spring.factories 中配置的 ApplicationContextInitializer 和 Applicat ...
随机推荐
- Qt编写自定义控件48-面板窗体控件
一.前言 很多时候需要有一个控件,能够替代容器控件,自动容纳多个widget,自适应宽高,然后提供滚动条功能,这就必然需要用到QScrollArea控件,可设置各个子面板的间距等,也在很多系统中用到, ...
- List根据多个字段分组
List<ClassEntity> distinctClass = classEntities.stream().collect(Collectors.collectingAndThen( ...
- confluent kafka connect remote debugging
1. Deep inside of kafka-connect start up To begin with, let's take a look at how kafka connect start ...
- PAT 甲级 1041 Be Unique (20 分)(简单,一遍过)
1041 Be Unique (20 分) Being unique is so important to people on Mars that even their lottery is de ...
- Select 优化
https://yq.aliyun.com/articles/704238?spm=a2c4e.11155472.0.0.66be4efeaUB5bk
- InfluxDB权限认证机制
一.介绍 权限认证机制,顾名思义,就是对 InfluxDB 数据库添加权限访问控制,在默认情况下,InfluxDB 的权限认证机制是关闭的,也就是说所有用户都有所有权限. 老规矩,直接实践上手,下图是 ...
- CYLTabBarController的简单使用
#pragma mark- 登录成功跳转至主页 -(void)jumpToMainVC { [UIApplication sharedApplication].statusBarStyle = UIS ...
- 为什么每次登录要手动 source /etc/profile ...
由于执行顺序如下,故追个查看以下文件,看看是不是 JAVA_HOME, PATH 等环境变量在后面的文件中被重写覆盖了. 1. /etc/profile2. /etc/environment3. ~/ ...
- eclipse 解决POM文件错误:org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project.MavenProject, org.apache.maven.archiver.MavenArchiveConfiguration)
解决方案: 更新eclipse中的maven插件 1.1 Help -> Install New Software -> Add 1.2 Location中输入 http://repo1. ...
- laravel进程管理supervisor的简单说明
原文地址:https://www.cnblogs.com/zhoujinyi/p/6073705.html 背景: 项目中遇到有些脚本需要通过后台进程运行,保证不被异常中断,之前都是通过nohup.& ...