likes
comments
collection
share

spring-创建Bean源码分析

作者站长头像
站长
· 阅读数 30

1.找到创建bean的入口

按照以下几个步骤找到preInstantiateSingletons()方法,创建bean就是在这个方法中。

1.进入AnnotationConfigApplicationContext的AnnotationConfigApplicationContext(Class<?>... componentClasses)方法

spring-创建Bean源码分析

2.进入refresh()方法中找到这个方法:finishBeanFactoryInitialization(beanFactory);

spring-创建Bean源码分析

3.进入finishBeanFactoryInitialization(beanFactory)方法中找到这个方法:beanFactory.preInstantiateSingletons();

spring-创建Bean源码分析

4.进入到 preInstantiateSingletons()方法

spring-创建Bean源码分析

2.创建bean

接下来就主要分析这个方法preInstantiateSingletons();

1.拿到beanName的集合

spring-创建Bean源码分析 beanDefinition在注册的时候会将beanDefinitionName存到beanDefinitionNames集合中。所以这里就直接使用beanDefinitionNames拿到beanName的集合,然后循环遍历

2.合并bean

RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

根据bean的名字通过这个方法(getMergedLocalBeanDefinition(beanName))得到一个新的RootBeanDefinition.

spring-创建Bean源码分析

spring-创建Bean源码分析

spring-创建Bean源码分析

先是创建了两个RootBeanDefinition 类型的对象,mbd 和 previous 接着判断了containingBd == null,这个判断在这个流程中肯定是 true ,因为传进来的就null。 所以基本都会走到这个判断里面,根据beanName从mergedBeanDefinitions中获取,然后给mbd赋值 接着会做一个判断,如果mbd有值,那么直接就返回。 然后会先将mbd 赋值给previous, 判断传进来bd(beanDefinition)有没有父beanDefinition。 如果有父beanDefinition那么就会先处理父beanDefinition,这里是递归调用的

spring-创建Bean源码分析 处理完父beanDefinition之后,就会转换为RootBeanDefinition并赋值。

mbd = new RootBeanDefinition(pbd); //转换
mbd.overrideFrom(bd);//赋值

如果没有父beanDefinition,那么就会直接转换为RootBeanDefinition。 最后会放到mergedBeanDefinitions中。

if (containingBd == null && isCacheBeanMetadata()) {
   this.mergedBeanDefinitions.put(beanName, mbd);
}

注意:这里的转换为RootBeanDefinition都是重新生成的,不会在原有的BeanDefinition上修改。

3.FactoryBean

spring-创建Bean源码分析

if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
  • !bd.isAbstract(): 不能是抽象的beanDefinition(这里指的不是抽象类)\
  • bd.isSingleton(): 必须是单例的\
  • !bd.isLazyInit():不能是懒加载的 只有符合上述条件才可以 然后会根据beanName判断是不是FactoryBean,如果是FactoryBean那么就进入了FactoryBean的创建过程。 先拼接&+beanName作为参数调用getBean(),这时候获取的bean是带有&的bean. 然后再判断是不是实现了FactoryBean接口,如果是会继续判断是不是实现了SmartFactoryBean接口(安全管理器先不考虑) 这个接口相比较FactoryBean接口可以指定isEagerInit的值(默认是false),如果是true,这时候会去调用getObject()方法,如果不是true ,会在这个时候:context.getBean("person");调用。

spring-创建Bean源码分析

spring-创建Bean源码分析

在调用getBean()方法的时候,无论是FactoryBean还是非FactoryBean(普通Bean)的创建都是在这个方法里面。 FactoryBean在创建的时候会先创建带&的bean.然后存到单例池(存入键不带&,在getBean方法中有对&的处理)。 接着会经过一系列的判断,先去factoryBeanObjectCache缓存中去取,如果没有然后在调用getObject()获取,并存入到factoryBeanObjectCache缓存中。

spring-创建Bean源码分析

spring-创建Bean源码分析

String beanName = transformedBeanName(name);

这行代码会对 &beanName 做处理

spring-创建Bean源码分析

spring-创建Bean源码分析

如果getCachedObjectForFactoryBean没有拿到,就会执行

object = getObjectFromFactoryBean(factory, beanName, !synthetic);

调用getObject()方法返回对象。

spring-创建Bean源码分析

spring-创建Bean源码分析

4.getBean(beanName)

spring-创建Bean源码分析

1. 处理beanName;

String beanName = transformedBeanName(name);

这个beanName会有这么几种情况:

1.FactoryBean: 会加&,例如:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
context.getBean("&lynnFactoryBean");
2.bean 有别名

例如:

@ComponentScan(value = "com.xl.service")
public class AppConfig {

   @Bean({"person","person1","person2"})
   public Person  person(){
      return  new Person();
   }
}

person 是bean的名字,person1才是bean名字,如果只有peson ,那么这个bean 是没有别名的。 spring存储别名是以这种方式存储的: spring-创建Bean源码分析 获取bean

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
context.getBean("person1");

2.从单例池获取,并判断

spring-创建Bean源码分析 这个判断里面的逻辑在FactoryBean中已经分析过了,这里就不说了。

3.父beanFactory

spring-创建Bean源码分析

BeanFactory parentBeanFactory = getParentBeanFactory();

先去父beanFactory获取,如果拿到就直接返回。正常情况下parentBeanFactory 肯定是空的。

public class Test {

   public static void main(String[] args)  {
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
      //context.getBean("&lynnFactoryBean");
      System.out.println(context.getBean("person"));
   }
}

spring-创建Bean源码分析

指定父BeanFactory

public class Test {

   public static void main(String[] args)  {
      AnnotationConfigApplicationContext per = new AnnotationConfigApplicationContext(AppConfigTest.class);
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      context.register(AppConfig.class);
      context.setParent(per);
      context.refresh();
      //context.getBean("&lynnFactoryBean");
      System.out.println(context.getBean("person"));
   }
}

spring-创建Bean源码分析

4.获取合并后的beanDefinition,并判断

RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);

获取合并后的beanDefinition,然后去判断beanDefinition是否是抽象的beanDefinition,如果是抽象的直接报错。

5.处理dependsOn注解。

spring-创建Bean源码分析

@Component
@DependsOn("company")
public class Person {
}

创建person的时候,检测到person依赖company,那么就会先创建company:

getBean(dep);

dep就是company,这样就又会到doGetBean方法。 注意:如果DependsOn注解引起的循环依赖是没办法解决的,直接报异常:

if (isDependent(beanName, dep)) {
   throw new BeanCreationException(mbd.getResourceDescription(), beanName,
         "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}

6.创建不同类型的bean

1.创建单例bean

spring-创建Bean源码分析

getSingleton() 中的lambda表达式返回的就是一个对象。先去单例池拿,拿不到用lambda表达式返回的bean. 然后还会执行

beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

以防止是FactoryBean.

2.创建原型bean

spring-创建Bean源码分析 原型bean就是直接使用createBean 创建,before是先标记,after是去除标记。

3.创建其他类型的bean

spring-创建Bean源码分析 这个地方就和springMvc有关了

7.createBean

无论是什么类的bean,都是要先去创建bean的,所以createBean就比较重要了。

1. 获取beanClass

获取类加载器,并通过类加载器以及全类名获取beanClass

spring-创建Bean源码分析

spring-创建Bean源码分析

spring-创建Bean源码分析 上图蓝框部分是某种情况下会执行。正常逻辑是执行

mbd.resolveBeanClass(beanClassLoader)

spring-创建Bean源码分析

getBeanClassName() 获取的值是在扫描的时候就已经赋值了,不过是String类型。

spring-创建Bean源码分析

2.实例化前

InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation

spring-创建Bean源码分析

执行示例化前这个方法,有返回对象,会直接返回对象。 通过一个示例来看下: 先实现这个接口覆盖实例化前的方法

@Component
public class LynnPostProcessor implements InstantiationAwareBeanPostProcessor {

   //初始化前
   @Override
   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      if(beanName.equals("person")){
         System.out.println(" before "+bean);
      }

      return bean;
   }
   //初始化后
   @Override
   public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      if(beanName.equals("person")){
         System.out.println("after hou:  "+bean);
      }
      return bean;
   }
   //实例化前
   @Override
   public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
      if(beanName.equals("person")){
         return new Person();
      }
      return null;
   }
}
@Component
public class Company {

   @Autowired
   private User user;
}
@Component
public class Person {

   @Autowired
   private Company company;
}
@Component
public class User {

   public void test(){
      System.out.println("123123123");
   }
}
public class Test {

   public static void main(String[] args)  {
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
      Company company = context.getBean("company", Company.class);
      User user = context.getBean("user", User.class);
      Person person = context.getBean("person", Person.class);
   }
}

Company,User,Person company 和 person 是有属性的。 在实例化前对person进行处理: 创建一个person返回。 compay是没有做处理的。 spring-创建Bean源码分析

执行后的结果: company的属性是有值的,person的属性是没值的。

spring-创建Bean源码分析

将实例化前对person处理的逻辑去掉,可以看到又有属性了。 所以,实例化前如果有返回对象,后续就不会执行spring创建bean的逻辑了。 接下来详细看下是怎么实现的:

spring-创建Bean源码分析

hasInstantiationAwareBeanPostProcessors()

spring-创建Bean源码分析

spring-创建Bean源码分析 上图这个方法是将所有beanPostProcessor分类缓存起来,以方便后续调用。 再看下图这个方法的

spring-创建Bean源码分析

spring-创建Bean源码分析

上图这里就是调用实例化前的地方,这个地方加循环是因为:会有很多地方实现这个接口,只要有一个实现有对象返回后续的就都不执行了。

3.实例化

spring-创建Bean源码分析

spring-创建Bean源码分析

if (instanceWrapper == null) {
   instanceWrapper = createBeanInstance(beanName, mbd, args);
}

这个就是创建bean实例,这里会处理@Bean注解,以及使用推断构造方法创建实例

4.处理已经合并过的BeanDefinition

MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()

spring-创建Bean源码分析

spring-创建Bean源码分析 可以这样使用 spring-创建Bean源码分析 这个地方是可以 修改beanDefinition的部分属性,作为扩展点。

5.处理循环依赖

spring-创建Bean源码分析

6.实例化后

InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()

spring-创建Bean源码分析

7.属性填充

spring-创建Bean源码分析

下图是不使用@Autowired就能进行属性填充, spring-创建Bean源码分析

如下图这样使用 spring-创建Bean源码分析

InstantiationAwareBeanPostProcessor.postProcessProperties() spring-创建Bean源码分析 这里是对带有@Autowired的属性进行填充

8.回调Aware

spring-创建Bean源码分析

spring-创建Bean源码分析

9.初始化前

spring-创建Bean源码分析

spring-创建Bean源码分析

10.初始化

spring-创建Bean源码分析

11.初始化后

spring-创建Bean源码分析

12.销毁

spring-创建Bean源码分析 用法:

spring-创建Bean源码分析

spring-创建Bean源码分析

销毁逻辑: 1.容器关闭的时候销毁。 2. 会先去判断bean有没有销毁逻辑 3. 如果有销毁逻辑会存到disposableBeans Map . 4. 当容器关闭时,会循环这个map缓存执行销毁逻辑,同时会将大部分缓存清空:单例池等。

转载自:https://juejin.cn/post/7153445932892651534
评论
请登录