一文带你理解Spring的重点


前言

本文基于Spring6的源码进行分析,主要解析Bean的生命周期,及比较重要的扩展点,和循环依赖问题的解析。


一、Bean的生命周期

在这里插入图片描述
我们看一下上面这张图片有几个比较关键的点我们来具体的解析一下

1.配置文件的读取 BeanDefinitionReader

可以看一下Spring常用的配置文件主要有xml、yml、properties,在使用Spring的开发过程中,我们大部分是使用xml文件来定义配置Bean,但是为了适配各种文件格式的配置文件,Spring中使用了统一的接口BeanDefinitionReader定义了对应的规则,具体的实现是由它的实现类来处理各种配置文件的读取,然后生成对应的BeanDefinition存储到Map中来方便后续Bean的生成。

2.BeanDefinition

BeanDefinition主要存放的就是bean的相关信息,主要参数都是我们通过xml配置文件进行配置的。
例如:

Generic bean: class [com.study.test.A]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [bean.xml]

3.IOC

我们都知道IOC是控制反转,DI是依赖注入。其中控制反转就是将Bean的创建交由IOC容器来处理,而不是通过之前new的方式来创建。这里可以吧IOC看作是一个容器,我们项目启动时通过反射来将我们配置好的bean构建好放到这个容器中,后续使用过程中我们不需要再通过new的方式来自己创建bean只需要从AppliationContext中直接获取即可。这里说一下另一个比较关键的类,BeanFactory
简单的说,我们可以把这个类看作是整个容器的入口,也是整个容器的根接口。

4.生命周期

在这里插入图片描述
Bean的生命周期主要可以分为下面几步
1:实例化(这里是通过反射的形式依据BeanDefinition中的信息构建出了对象)
注意:这里仅仅是在内存中开辟了空间,对象中的属性都没有赋值都是初始值
2:属性赋值-1(开始bean对象中的属性开始赋值)
3:属性复制-2(针对实现了Aware接口相关子类的扩展点进行处理)
注意:其实此时Bean对象已经初始化完成,可以使用了后续的操作指示为了扩展使用。
4:执行前置处理方法
5:执行初始化方法(也就是我们在xml里面制定的init-method)
6:执行后置处理方法
7:使用对象
8:销毁对象

二、重要的扩展点

1.BeanPostProcessor

BeanPostProcessor:主要是对对象创建过程中的前置扩容点和后置扩容点的扩展。

public class C implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

2.BeanFactoryPostProcessor

BeanFactoryPostProcessor:主要是Bean对象的扩展点,可以获取到对应的BeanDefinition信息,进行对应的配置。

public class MyBeanPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition a = beanFactory.getBeanDefinition("A");
        System.out.println("设置BeanFactory");
    }
}

2.ClassPathXmlApplicationContext

我们可以通过继承ClassPathXmlApplicationContext对象重写initPropertySources、customizeBeanFactory方法来进行属性参数的扩展。

public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
    
    @Override
    protected void initPropertySources() {
        System.out.println("extend initPropertySources");
        getEnvironment().setRequiredProperties("abc");
    }

    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        beanFactory.setAllowBeanDefinitionOverriding(false);
        beanFactory.setAllowCircularReferences(false);
        // 必须要加这一行
        super.customizeBeanFactory(beanFactory);
    }
}

三、循环依赖

在这里插入图片描述
如上图,A对象中定义了B类型的属性的b,B对象中定义了A类型的属性A,然后在xml配置文件中,分别注入A、B两个对象,这样就构成了循环依赖。
接下来我们看一下Spring是如何通过3级缓存来解决循环依赖的:

// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
// 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
// 二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);

这里比较重要的是三级缓存Value的类型为ObjectFactory<?>,可以看到这是一个函数式接口,所以只有当我们调用这个getObject方法的时候,我们所传入的lamada表达式(()-> getEarlyBeanReference(beanName,mbd,bean))才会真正被执行。

@FunctionalInterface
public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}

下面是A、B两个对象在缓存中的的存在过程(红色的模块为删除的模块)
在这里插入图片描述
这里有几个问题特殊说明一下:
(1)三个缓存查找对象的顺序是什么?
答:先从一级缓存开始查找,如果找不到,在二级缓存中查找,最后在三级缓存中查找
(2)如果只有一个缓存map能否解决循环依赖的问题?
答:不可以,如果只有一个map那么半成品和成品对象都放到一个map中,但是半成品对象不能提供给外部使用,所以必须区分出来,否则可能会有暴露半成品的风险。
(3)如果只有两个map能否解决循环依赖的问题?
答:能,但前提条件是,不可以包含AOP的操作,因为传统的生命周期AOP的操作是在对象初始化完成后,在相应的扩展点进行AOP代理的,而此时我们的对象仅仅是实例化结束,还没有进行赋值操作,所以当前对象还未初始化完成,无法进行AOP操作,而三级缓存因为在value的函数式接口中判断了是否需要创建代理的关系,所以不会出现问题。
(4)为什么三级缓存就可以解决循环依赖中包含代理对象的问题?
答:如果需要代理对象,那么在代理对象创建之后就需要覆盖原始对象。。
(5)在对外暴漏时如何准确
答:lamada表达式类似是一种回调机制,在确定要对外暴露时就唯一性的确定,到底是代理对象还是原始对象,这就是为什么不把对象直接放入二级缓存中,而是通过三级缓存的lamada表达式的方式来执行。

四、BeanFactory和FactoryBean的区别

BeanFactory:是遵守完整的Bean生命周期来创建Bean。
FactoryBean:是私人定制的方式来创建Bean。
例入:我们需要实现FactoryBean接口,重写对应的方法。

public class AFactoryBean implements FactoryBean<A> {
    @Override
    public A getObject() throws Exception {
        return new A();
    }

    @Override
    public Class<?> getObjectType() {
        return A.class;
    }

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值