"/>
侧边栏壁纸
博主头像
PySuper 博主等级

千里之行,始于足下

  • 累计撰写 286 篇文章
  • 累计创建 17 个标签
  • 累计收到 2 条评论

目 录 CONTENT

文章目录

Java Spring 核心 一

PySuper
2023-01-07 / 0 评论 / 0 点赞 / 9 阅读 / 7127 字
温馨提示:
所有牛逼的人都有一段苦逼的岁月。 但是你只要像 SB 一样去坚持,终将牛逼!!! ✊✊✊

Spring 核心

@Configuration

@Configuration 注解 -【Spring 底层原理】open in new window

@Configuration 注解底层是如何实现的,通过源码咱们可以反推并总结为以下几点:

  • Spring 首先会获取到所有的 beandefenition

  • ConfigurationClassUtils 类中 checkConfigurationClassCandidate 方法判断是 Full @Configuration 还是 lite @Bean mode

  • 通过 ConfigurationClassPostProcessor 后置处理器遍历所有的 beandefenition

  • 将标记了 Full @Configuration 模式的 beandefenition,会对这个类进行 cglib 代理,生成一个代理类,并把这个类设置到 BeanDefenition 的 Class 属性中

  • 配置类会被 CGLIB 增强 (生成代理对象),放进 IoC 容器内的是代理

  • 对于内部类是没有限制的:可以是 Full 模式或者 Lite 模式

  • 配置类内部可以通过方法调用来处理依赖,并且能够保证是同一个实例,都指向 IoC 内的那个单例

  • 需要用这个 Bean 实例的时候,从这个 Class 属性中拿到的 Class 对象进行反射,最终反射出来的是代理增强后的类

  • 通过 @Configuration 标注类的 Bean,Spring 会先去容器中查看是否有这个 Bean 实例,如果有就返回已有的对象,没有就创建一个,然后放到容器中

IOC 加载原理

SimpleAnnotationCtxMainopen in new window

详细可参考源码理解(源码分析 2 容器的基本实现、3 默认标签的解析、5 bean 的加载 小节有详细代码分析步骤)

也可根据如上入口 DEBUG 加深理解,以下是一些步骤说明:

  • new AnnotationConfigApplicationContext()

  • 将配置、注解的信息读取,加载为 BeanDefinition 对象,这些信息包括 Bean 的类名、属性、构造函数参数等

    • 注意 AnnotationConfigApplicationContext 该步骤的代码,也在 AbstractApplicationContext.invokeBeanFactoryPostProcessors 子逻辑块里面

  • 注册 BeanDefinition 到 DefaultListableBeanFactory.beanDefinitionMap 中 (bean 为定义态)

    • XmlBeanFactory 注册方法 BeanDefinitionReaderUtils.registerBeanDefinition ()

    • AnnotationConfigApplicationContext 注册方法在 AbstractApplicationContext.refresh () 里面的 invokeBeanFactoryPostProcessors (beanFactory) 中,实际注册代码就比较深了,略

  • 循环通过 BeanFactory.getBean () 创建 Bean (注意是非懒加载的,可以看 AbstractApplicationContext.getBean () 方法源码)

  • 进入 AbstractBeanFactory.doGetBean 获取 Bean。这里会出现经典的循环依赖问题,后续详细介绍

    • DefaultSingletonBeanRegistry.singletonObjects (bean 为成熟态) 中获取,拿到则直接返回,结束 5 步骤,拿不到进入 5.2

    • 实例化 Bean (反射,属性值都为空,纯净态)

    • 依赖注入。循环依赖问题:如果此时 A 依赖 B,B 依赖 A,则 getBean (A) 会调用 getBean (B) 且 getBean (B) 又会调用 getBean (A)

    • 初始化。进行方法回调,如 @PostConstruct 进行初始化,注意这里在依赖注入后,已经可以拿到依赖注入的对象了。可以在这里进行数据库连接池、线程池的初始化的业务逻辑

    • 创建动态代理对象。BeanPostProcessor.postProcessAfterInitialization 里面创建了动态代理 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)) 触发,对应源码 AbstractAutoProxyCreator.wrapIfNecessarycreateProxy 代码块

    • 动态代理对象放入 DefaultSingletonBeanRegistry.singletonObjects 中

对比可以理解如下问题,如

  • Spring 国际化在哪一步处理 (AbstractApplicationContext.initMessageSource)

ApplicationContext 和 BeanFactory 区别

  • 都是容器,是父子关系

  • ApplicationContext 提供上层程序员用的,功能更加全面,如事件监听、多语言等

  • BeanFactory 在底层真正干活的

Bean 的生命周期

可以参考源码分析部分 5 bean 的加载 理解。以下是核心步骤。未做额外说明,则都在 AbstractAutowireCapableBeanFactory 类中。

  • 实例化,可以拆为两步说

    • 推断构造方法

    • 实例化

  • Bean 属性注入,详见 populateBean ()

  • 初始化,对应代码为 initializeBean,相关步骤如下:

    • Aware 相关处理 invokeAwareMethods

      • ((BeanNameAware) bean).setBeanName(beanName)

      • ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl)

      • ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this)

    • 调用 BeanPostProcessor 的预初始化方法 applyBeanPostProcessorsBeforeInitialization@PostConstruct 也是初始化前处理

    • 调用初始化方法 invokeInitMethods

      • 调用 InitializingBean 初始化方法 ((InitializingBean) bean).afterPropertiesSet()

      • 调用自定义初始化方法 invokeCustomInitMethod(beanName, bean, mbd)

    • 调用 BeanPostProcessor 的初始化后方法 applyBeanPostProcessorsAfterInitialization

  • Bean 可以使用了,通过 DefaultSingletonBeanRegistry.getSingleton 添加普通对象或代理对象到容器中

  • 容器关闭

    • 调用 DisposableBean.destroy ()。DestructionAwareBeanPostProcessor.postProcessBeforeDestruction 被调用

    • 调用自定义销毁方法,没找到代码入口,可以参考 DisposableBeanAdapter.destroy() 代码理解

实例化

实例化代码可以从 AbstractBeanFactory.doGetBean 里面的 AbstractAutowireCapableBeanFactory.createBean 方法开始跟踪

  • 准备工作

    • 获取子类和父类已经合并的 RootBeanDefinition

      • 详见 AbstractBeanFactory.doGetBean 的 RootBeanDefinition mbd = getMergedLocalBeanDefinition (beanName)

    • 实例化

      • 如下链路可以找到 BeanUtils.instantiateClassctor.newInstance(argsWithDefaultValues),即反射加载代码

      • 链路如下

        • 1.2.1 AbstractAutowireCapableBeanFactory.createBean

        • 1.2.2 AbstractAutowireCapableBeanFactory.doCreateBean

        • 1.2.3 AbstractAutowireCapableBeanFactory.createBeanInstance

        • 1.2.4 AbstractAutowireCapableBeanFactory.instantiateBean

        • 1.2.5 AbstractAutowireCapableBeanFactory.autowireConstructor。推断构造方法

        • 1.2.6 SimpleInstantiationStrategy.instantiate

        • 1.2.7 BeanUtilseanUtils.instantiateClass(constructorToUse)

如果是非普通对象,则会将代理对象创建,见 getEarlyBeanReference(beanName, mbd, bean))

推断构造方法

Spring 之推断构造方法底层原理详解

实例化 SimpleInstantiationStrategy.createBeanInstance 里面有推断构造方法底层源码

有点复杂,可以看文章理解 Spring 源码篇之推断构造方法 open in new window

优先级处理

对应源码在 AbstractApplicationContext.refresh () 进去,找到 AbstractApplicationContext.registerBeanPostProcessors ()

加载顺序如下:

  1. 先注册 @PriorityOrdered 的 Bean

  2. 再注册 @Ordered 的 Bean

  3. 后注册其他 Bean

这篇文章有图,结合源码理解也挺好,Spring Bean 的完整生命周期(带流程图,好记)open in new window

额外有些内容补充如下

循环依赖

手写循环依赖简单代码,我们用一级缓存解决了循环依赖 (仅限死循环问题,Spring 处理场景更复杂)

SimCircularCtxMainopen in new window

Spring 如何利用多级缓存解决循环依赖

用了 3 个 Map 即 3 级缓存处理

  • 1 级缓存 singletonObjects

  • 2 级缓存 earlySingletonObjects

  • 3 级缓存 singletonFactory

  • 首先尝试从 singletonObjects 里面获取实例,如果获取不到再从 earlySingletonObjects 里面获取

  • 如果还获取不到,再尝试从 singletonFactories 里面获取 beanName 对应的 ObjectFactory

  • 然后调用这个 ObjectFactory 的 getObject 来创建 bean,并放到 earlySingletonObjects 里面去,并且从 singletonFactories 里面 remove 掉这个 ObjectFactory (1、2 级缓存互斥,原因大概率是因为初始化完成后生成了代理)

一级缓存缓存完全体的 bean 对象

二级缓存是解决并发环境下锁性能问题

三级缓存是解决 AOP 问题,以及循环依赖问题。保证了二级缓存 getEarlySingletonObjects 获取的也是 AOP 代理对象,这样就和一级缓存对象对应上了。注意预期只有循环依赖的 bean 才进行动态代理,普通的 bean 依然在初始化后才去创建动态代理,这块逻辑对应 AbstractAutoProxyCreator.earlyProxyReferences 代码控制

三级缓存见示例代码 SimCircular3Ctxopen in new window

三级缓存示例代码如下

public class SimCircular3Ctx {

    private Map<String, BeanDefinition> beanDefinitionMap = new LinkedHashMap<>();

    // 一级缓存,最终完整 bean 对象
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    private Map<String, ObjectFactory> factoriesObjects = new ConcurrentHashMap<>();

    public SimCircular3Ctx() throws Exception {
        // 加载 ioc 容器
        refresh();
    }

    private void refresh() throws Exception {
        // 读取 BeanDefinition
        loadBeanDefinitions();

        // 创建 bean
        finishBeanFactoryInitialization();
    }

    private void loadBeanDefinitions() {
        RootBeanDefinition beanDefinition1 = new RootBeanDefinition(AService.class);
        RootBeanDefinition beanDefinition2 = new RootBeanDefinition(BService.class);
        beanDefinitionMap.put("aService", beanDefinition1);
        beanDefinitionMap.put("bService", beanDefinition2);
    }

    private void finishBeanFactoryInitialization() throws Exception {
        for(String beanName : beanDefinitionMap.keySet()){
            getBean(beanName);
        }
    }

    // 方法如果加 synchronized 会有性能问题,因此有了二级缓存
    public Object getBean(String beanName) throws Exception {
        // 1 获取
        Object bean = getSingleton(beanName);
        if(Objects.nonNull(bean)){
            return bean;
        }
        synchronized (singletonObjects){
            bean = getSingleton(beanName);
            if(Objects.nonNull(bean)){
                return bean;
            }
        }
        // 2 创建
        // 2.1 实例化
        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
        Class<?> beanClass = beanDefinition.getBeanClass();
        Object beanInstance = beanClass.getConstructor().newInstance();

        // put 到三级缓存
        // 预期只有循环依赖的 bean 才进行动态代理,普通的 bean 依然在初始化后才去创建动态代理
        factoriesObjects.put(beanName, () -> new JdkProxyBeanPostProcessor().getEarlyBeanReference(beanInstance, beanName));

        // 2.2 依赖注入 @Autowired
        for(Field declaredField : beanClass.getDeclaredFields()){
            if(declaredField.getAnnotation(Autowired.class) != null){
                String name = declaredField.getName();

                // bytype byname
                Object dependBean = getBean(name);

                declaredField.setAccessible(true);
                declaredField.set(beanInstance, dependBean);
            }
        }

        // 2.3 初始化 aop

        // 3 put 到一级缓存
        singletonObjects.put(beanName, beanInstance);
        earlySingletonObjects.remove(beanName);
        return beanInstance;
    }

    private Object getSingleton(String beanName) {
        if(singletonObjects.containsKey(beanName)){
            return singletonObjects.get(beanName);
        }
        // spring 源码锁的都是 singletonObjects
        synchronized (singletonObjects){
            if(earlySingletonObjects.containsKey(beanName)){
                return earlySingletonObjects.get(beanName);
            }
            if(factoriesObjects.containsKey(beanName)){
                ObjectFactory objectFactory = factoriesObjects.get(beanName);
                Object aopObject = objectFactory.getObject();
                // 存二级缓存,防止循环依赖多次执行 ObjectFactory
                earlySingletonObjects.put(beanName, aopObject);
                return aopObject;
            }
        }
        return null;
    }

}
<已自动折叠>

Spring 二级缓存能否解决循环依赖

可以,但是就是实例化之后,就要立即创建 AOP 代理了

另外注意下以下一些点

  • 一级缓存其实就可以解决死循环依赖问题了,但是在多线程环境下要加如 synchronized 锁保证线程安全

  • 二级缓存是在并发环境下解决锁粒度问题 (容器设置为懒加载就会有并发加载的问题)

Spring 的单例如何保证线程安全

用的双重检查锁保证,参考 Demo 理解 SimCircular2Ctxopen in new window

Spring 是否解决多例 Bean 的循环依赖

无法解决

如果是原型 bean,那么就意味着每次都要去创建对象,无法利用缓存,缓存只能在实例化后判断处理

Spring 是否解决构造函数中的循环依赖

无法解决

如果是构造方法注入,那么就意味着需要调用构造方法注入,也无法利用缓存,缓存只能在实例化后判断处理

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区