简单了解IoC

简单了解IoC想一想如果只有二级缓存 再加上 AOP 代理 此时 B a 是半成品 A 而 A 是个代理对象 这两个不相同 即 b getA Aa 而 A 是个单例对象 这就很奇怪了吧

大家好,欢迎来到IT知识分享网。

IoC

什么是IoC?

IoC(Inversion of Control),即控制反转,这是一种设计思想,在Spring指将对象的控制权交给Spring容器,由容器来实现对象的创建、管理,程序员只需要从容器获取想要的对象就可以了。这里引申一个词叫DI(Dependency Injection),即依赖注入,他是IoC的一种具体实现方式,类似于Map和HashMap的关系,指对象创建时的属性赋值交给Spring容器来做。

还有一种理解是把IoC理解为Spring容器,底层是一个Map,key是beanName,value是bean。

为什么需要IoC?

  • 不用手动new对象,将对象创建和业务解耦
  • 无需关注对象创建的过程,只管使用就行

IoC怎么使用?

声明bean

xml文件

@Component及其衍生注解

@Configuration和@Bean

获取bean

beanFactory.getBean(String beanName)

applicationContext.getBean(String beanName)

属性注入注解方式

@Autowired、@Value

IoC底层实现?

IoC把对象控制权反转给Spring框架,那么我们就来关注一下Spring怎么实现bean的创建、管理等,再详细一些,bean从实例化 -> 属性赋值 -> 初始化 -> 使用 -> 销毁,这叫做bean的生命周期,Spring管理的bean主要走的就是这么个流程。

什么是属性赋值前面已经解释过了,实例化和初始化是怎么回事呢?

  • 实例化实际上就是bean的创建,给bean在内存中分配空间;
  • 初始化回调各种Aware接口、回调各种初始化方法、生成AOP代理对象也在该阶段进行,该阶段主要是完成初始化回调。

基本概念

接下来会介绍底层源码,在此之前会给出一些基本概念,以便更加清晰地了解源码。

  • BeanFactory:bean工厂,用来生成bean,故bean的实例化、属性赋值、初始化都在此进行
  • BeanDefinition:bean的定义信息,你想让BeanFactory生产bean,那么自然要有张图纸,这张图纸就是BeanDefinition
  • BeanPostProcessor:bean的增强器,用来执行一些增强方法
  • BeanFactoryPostProcessor:beanFactory的增强器,用来执行一些增强方法

bean生命周期详解

refresh ()的第11步finishBeanFactoryInitialization(beanFactory);这里会进行bean的生命周期

通过getBean(beanName);进行单例bean的初始化,这里有对工厂bean的特殊处理TODO

getBean顾名思义就是获取bean,引出两个问题,去哪里获取?获取不到怎么办?

  • Spring里面大量使用缓存,这里存储bean的缓存特指Map,通过beanName为key可以获取到对应的bean;
  • 如果本身缓存不存在bean,那么需要去创建bean并将其放到缓存中去。

走从缓存中拿的逻辑

getSingleton(String beanName)会尝试从缓存中获取bean,值得一提的是这里有三个缓存,俗称三级缓存,为什么要使用三级缓存,等到后面再说。

走bean创建的逻辑doCreateBean

getSingleton, ObjectFactory<?> singletonFactory)内部会调用createBean()去创建bean,并且创建后把bean放到缓存中去

接下来就是bean的生命周期了:

  • 实例化:一般是反射调用无参构造器创建对象,instanceWrapper = createBeanInstance(beanName, mbd, args);
  • 属性赋值:populateBean(beanName, mbd, instanceWrapper);
  • 初始化:对bean进行一些额外操作,exposedObject = initializeBean(beanName, exposedObject, mbd);
  • 销毁:
实例化详解

前面提到实例化时通过构造器实现,不通过直接new对象的方式,我们该如何创建对象呢?Spring用到了反射去实例化bean,要用反射自然少不了bean对应的Class对象,通过内存中的Class对象我们才能够获取到类的信息(一个对象的模板)从而创建对象。

  • 获取bean的Class对象
    • Class<?> beanClass = resolveBeanClass(mbd, beanName);
  • 还可能用工厂去创建bean
  • 可能这里会用有参构造器,如果你有编写有参构造器的话
  • 然后到是无参构造器构造
    • return instantiateBean(beanName, mbd);
    • constructorToUse = clazz.getDeclaredConstructor();
3种实例化方式

除了构造器之外可以使用工厂方法去进行bean的实例化,以下列出他们各自的优缺点

  • 构造器:反射使用类构造器
    • 优点:使用方便,简单明了
    • 缺点:如果创建逻辑复杂,无法定制化配置
  • 静态工厂:调用一个静态的方法
    • 优点:可以在创建bean的时候获取到一些静态资源
    • 缺点:需要提前写好,无法拓展
  • 普通工厂:调用一个普通的方法
    • 优点:适用于逻辑复杂的创建,如依赖注入或状态管理,如某个依赖需要通过入参判断注入哪个类
    • 缺点:相比构造器方式需要多写一个工厂类

总的来说,如果没有特别复杂的创建逻辑需求,直接用构造器创建就好。

策略模式

值得一提的是这里用到了策略模式

Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);

下面这张图是继承关系图:顶级策略接口InstantiationStrategy

image-20240620223742711

默认JDK的无参构造器就是在SimpleInstantiationStrategy类中实现,如果实现CGLIB的方式实例化,在CglibSubclassingInstantiationStrategy有内部类CglibSubclassCreator通过instantiate(@Nullable Constructor<?> ctor, Object... args)实现CGLIB的一个实例化操作

属性赋值详解

对象已经在内存中存在了,但是它的属性还是空的,我们需要对其进行属性赋值,populateBean(beanName, mbd, instanceWrapper);,可以思考几个问题,值存放在哪里?如何赋值?

这些值是存放在BeanDefinition的成员变量里面的MutablePropertyValues propertyValues,通过这个就可以获取到bean的属性,在xml中是以<property>标签表示,如果你需要用注解的方式实现,可以自定义注解,搭配自定义BeanFactoryPostProcessor

 @Target(ElementType.FIELD)  @Retention(RetentionPolicy.RUNTIME)  @Documented  public @interface MyIntegerValue {  ​      int value() default 0;  }  ​  @Component  public class MyValueBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {  ​   @Override   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {   try {   BeanDefinition beanDefinition =  registry.getBeanDefinition("person");   String beanClassName = beanDefinition.getBeanClassName();   Class<?> beanClass = Class.forName(beanClassName);   Field field = beanClass.getDeclaredField("num");   MyIntegerValue annotation = field.getAnnotation(MyIntegerValue.class);   int value = annotation.value();   beanDefinition.getPropertyValues().getPropertyValueList().add(new PropertyValue(field.getName(), value));   } catch (NoSuchFieldException | ClassNotFoundException e) {   throw new RuntimeException(e);   }   }  }

如何赋值?

setPropertyValues(PropertyValues pvs)setPropertyValue(PropertyValue pv)

 protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {      if (tokens.keys != null) {          // 设置数组、List、Map,暂时TODO         processKeyedProperty(tokens, pv);     }      else {          // 设置普通值,就是调用反射,method.invoke(obj, arg)         processLocalProperty(tokens, pv);     }  }

对于@Autowired的注解,通过InstantiationAwareBeanPostProcessor的postProcessProperties()方法进行赋值

 if (hasInstantiationAwareBeanPostProcessors()) {      if (pvs == null) {         pvs = mbd.getPropertyValues();     }      for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {         // 对所有需要依赖检查的属性进行后处理         PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);         if (pvsToUse == null) {            return;         }         pvs = pvsToUse;     }  }

初始化详解

属性赋值之后,我们还需要对其进行初始化,可以调用初始化方法实现一些逻辑,例如资源分配或者初始化,如Spring Boot的自动配置类,exposedObject = initializeBean(beanName, exposedObject, mbd);

调用用户自己实现的初始化方法invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)

  1. 先调用 @PostConstructor 注解,在BeanPostProcessor前置增强执行
  2. 再调用实现 InitializingBean 接口的回调方法 afterPropertiesSet()
  3. 最后调用xml文件的 init-method 方法
初始化增强

在初始化前后,会对bean进行增强

前置增强

一般是一些Aware回调方法执行,如ApplicationContextAware的回调方法的执行,调用BeanPostProcessor增强processor.postProcessBeforeInitialization(result, beanName);

后置增强

这里会和AOP有关了,调用BeanPostProcessor增强,processor.postProcessAfterInitialization(result, beanName)

为什么使用三级缓存?
循环依赖

这个三级缓存其实和属性赋值那一步相关,缓存的是bean,那我们属性赋值的时候有可能是给基本属性赋值、也有可能是给引用属性赋值,在给引用属性赋值的时候我们会调用getBean(),是不是很熟悉,即创建bean的时候会调用的,从某种角度来看很像递归,肯会出现一直调用导致SOF,在Spring表现的就是会出现循环依赖问题,那我们就需要一个终止条件,即用缓存代替创建bean

具体是哪三级缓存?
 / Cache of singleton objects: bean name to bean instance. */  // 一级缓存,beanName -> 实例化并且初始化的成品  private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);  ​  / Cache of singleton factories: bean name to ObjectFactory. */  // 三级缓存,beanName -> ObjectFactory  private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);  ​  / Cache of early singleton objects: bean name to bean instance. */  // 二级缓存,beanName -> 实例化但是未初始化的成品  private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

为什么用了三级缓存就没有循环依赖?

先看循环依赖的原因:假设有A,B两个类,他们互相依赖

1.实例化A

2.给A的b属性赋值

3.实例化B

4.给B的a属性赋值

5.重复第一步

那用二级缓存能否解决循环依赖呢?

1.实例化A

2.给A的b属性赋值

3.实例化B

4.给B的a属性赋值(注意,此时的a就不会再走到第五步去实例化了,而是从二级缓存中获取完成品A)

5.B的属性赋值完成,接下来回到第二步,即将A的属性赋值完成,自此循环依赖解决

那为什么是三级缓存呢?

想一想如果只有二级缓存,再加上AOP代理,此时B.a是半成品A,而A是个代理对象,这两个不相同,即b.getA() != A a

而A是个单例对象,这就很奇怪了吧。

那你可能又会想那实例化的时候我直接先把代理对象创建出来不久好了吗,不就一样了吗,但是objectFactory每次调用getObject()返回的都是新的代理对象,所以如果某个代理类C被当作很多个类的属性,那么这些类的这个引用属性C还是不相同的,所以我们需要三级缓存,有了二级就不用找三级,即代理对象只会被生产一次

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/124581.html

(0)
上一篇 2025-10-06 12:15
下一篇 2025-10-06 12:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信