Spring: Bean的创建原理解析


一、Spring创建Bean流程

在这里插入图片描述
1.读取Bean的定义信息
通过BeanDefinitionReader这个接口解析xml配置、配置类或其他的一些方式定义的类,得到BeanDefinition(Bean定义信息)

2.实例化Bean
通过BeanPostProcessor这个接口(增强器)可以对我们的BeanDefinition进行一些修改,然后BeanFactory通过反射实例化Bean对象,但是此时的Bean对象还没有进行初始化,没有填充属性等操作。

3.初始化Bean
(1)自定义属性赋值是用 set 方法赋值(populateBean())
(2)容器对象的属性赋值是用实现Aware接口的方式来赋值(invokeAwareMethods()),如BeanNameAware
(3)调用BeanPostProcessor的前置处理方法
(4)调用init初始化方法:init-method
(5)调用BeanPostProcessor的后置处理方法(AOP在这里实现)
(6)获得一个完整的对象,并将对象放入map中(通过Context.getBean()可以获取到Bean对象并使用)

4.销毁Bean
Spring容器关闭时会调用DisposableBean的Destory()方法
如果你在这个Bean中配置了destory-method属性,会自动调用指定的销毁方法

二、Bean的整体创建流程

在这里插入图片描述

  • 1.利用该类的构造方法来实例化得到一个对象(但是如果一个类中有多个构造方法, Spring则会进行选择,这个叫做推断构造方法,下文在详细介绍)
  • 2.得到一个对象后,Spring会判断该对象中是否存在被@Autowired注解了的属 性,把这些属性找出来并由Spring进行赋值(依赖注入)
  • 3.依赖注入后,Spring会判断该对象是否实现了BeanNameAware接口、 BeanClassLoaderAware接口、BeanFactoryAware接口,如果实现了,就表示当前 对象必须实现该接口中所定义的setBeanName()、setBeanClassLoader()、 setBeanFactory()方法,那Spring就会调用这些方法并传入相应的参数
  • 4.Aware回调后,Spring会判断该对象中是否存在某个方法被@PostConstruct注解 了,如果存在,Spring会调用当前对象的此方法(初始化前),上面的代码中初始化了一个adminUser的数据。
  • 5.紧接着,Spring会判断该对象是否实现了InitializingBean接口,如果实现了,就 表示当前对象必须实现该接口中的afterPropertiesSet()方法,那Spring就会调用当
    前对象中的afterPropertiesSet()方法(初始化)
  • 6.最后,Spring会判断当前对象需不需要进行AOP,如果不需要那么Bean就创建完 了,如果需要进行AOP,则会进行动态代理并生成一个代理对象做为Bean(初始化 后)
    关于第6步控制台没办法打出日志,因为初始后涉及到spring的源码操作,但是可以通过断点看一下,提一句例子中UserService是被LogAspect切面切的。

这样,一个Bean就创建完了,如果当前Bean是单例Bean,那么会把该Bean对象存入一个Map<String, Object>,Map的key为beanName,value为Bean对象。这样下次getBean时就可 以直接从Map中拿到对应的Bean对象了。(实际上,在Spring源码中,这个Map就 是单例池)
如果当前Bean是原型Bean,那么后续没有其他动作,不会存入一个Map,下次 getBean时会再次执行上述创建过程,得到一个新的Bean对象。

三、推断构造方法

至此,我们清楚了Bean的创建流程,那如果UserService中有多个构造函数呢?第一步还能顺利的创建一个普通对象吗?这里面涉及到一个概念推断构造方法,就是spring会去推断用哪个构造方法来创建出普通对象。

总结下:

  • 如果一个类只有一个构造方法,那么没得选择,只能用这个构造方法。但是有参的构造方法,参数必须是spring的Bean这样spring才能拿到进行赋值。
  • 如果一个类存在多个构造方法,Spring不知道如何选择,就会看是否有无参的构 造方法,因为无参构造方法本身表示了一种默认的构造方法。
  • 如果都没有构造方法,就是用默认的无参构造方法来创建。
  • 其实多个构造函数,也可以手动指定告诉spring用哪个构造函数来创建,那就是加了@Autowired注解

四、依赖注入流程

不管是属性注入还是构造方法注入,能提供的信息只有两个一个是类型OrderService ,一个是名字orderService。那到底是根据类型注入的还是根据名字注入的呢?
假设根据名字注入的那刚好有一个其他类型的Bean名字也叫orderService那注入的时候岂不是会类型不匹配异常。比如说刚好有一个OrderBaseService类但是beanName也叫orderService,如果根据名字注入的话拿到的是OrderBaseService对象显然类型不匹配。所以注入通常是先根据类型来查找的:

  • 先根据入参类型找,如果只找到一个不用管name,那就直接用来作为入参
  • 如果根据类型找到多个,则再根据入参名字来确定唯一
  • 最终如果没有找到,则会报错,无法创建当前Bean对象

五、代理对象生成

代理对象通常是AOP的时候会生成代理对象还有一种就是开启事务的时候也会生成代理对象。否则的话Bean都是直接根据构造函数生成对象在进行依赖注入和初始化等流程。

1.AOP代理对象生成

AOP就是进行动态代理,在创建一个Bean的过程中,Spring在最后一步会去判断当前正在 创建的这个Bean是不是需要进行AOP,如果需要则会进行动态代理。

如何判断当前Bean对象需不需要进行AOP:

  • 1.找出所有的切面Bean
  • 2.遍历切面中的每个方法,看是否写了@Before、@After等注解
  • 3.如果写了,则判断所对应的Pointcut是否和当前Bean对象的类是否匹配
  • 4.如果匹配则表示当前Bean对象有匹配的的Pointcut,表示需要进行AOP

利用cglib进行AOP的大致流程:

  • 1.生成代理类UserServiceProxy,代理类继承UserService
  • 2.代理类中重写了父类的方法,比如UserService中的test()方法
  • 3.代理类中还会有一个target属性,该属性的值为被代理对象(也就是通过 UserService类推断构造方法实例化出来的对象,进行了依赖注入、初始化等步骤的 对象)
  • 4.代理类中的test()方法被执行时的逻辑如下:
    • a. 执行切面逻辑(@Before)
    • b. 调用target.test()
      当我们从Spring容器得到UserService的Bean对象时,拿到的就是UserServiceProxy所生 成的对象,也就是代理对象。
      UserService代理对象.test()—>执行切面逻辑—>target.test(),注意target对象不是代理 对象,而是被代理对象。

2.事务代理对象生成

Spring事务 当我们在某个方法上加了@Transactional注解后,就表示该方法在调用时会开启Spring事 务,而这个方法所在的类所对应的Bean对象会是该类的代理对象。
Spring事务的代理对象执行某个方法时的步骤:

  • 1.判断当前执行的方法是否存在@Transactional注解
  • 2.如果存在,则利用事务管理器(TransactionMananger)新建一个数据库连接,
  • 3.修改数据库连接的autocommit为false
  • 4.执行target.test(),执行程序员所写的业务逻辑代码,也就是执行sql
  • 5.执行完了之后如果没有出现异常,则提交,否则回滚

注意:Spring事务是否会失效的判断标准:某个加了@Transactional注解的方法被调用时,要判 断到底是不是直接被代理对象调用的,如果是则事务会生效,如果不是则失效。

总结

Spring中Bean的创建过程其实就是从一个普通对象蜕变成Bean的一个过程,蜕变包括依赖注入,初始化等步骤。最后在看下这个类是否有被AOP或开启事务有的话会额外生成代理对象作为Bean。

其他

参考:https://www.jianshu.com/p/57ed586e258c
https://www.csdn.net/tags/MtjaYgxsOTU3NTMtYmxvZwO0O0OO0O0O.html

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Freedom3568

技术域不存在英雄主义,不进则退

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值