Spring 源码分析(三)—— 三级缓存与循环依赖

Spring 源码分析(三)—— 三级缓存与循环依赖,第1张

Spring 源码分析(三)—— 三级缓存循环依赖

建议从下文开始阅读。
Spring源码 v1.0 —— 简陋版
Spring源码 v2.0 —— applicationContext

循环依赖 什么是循环依赖?

如下BeanA类依赖了BeanB类,同时BeanB又依赖了BeanA类,这种依赖关系就形成了一个闭环骂我们把这种依赖关系称为循环依赖。

循环依赖问题复现 业务模块代码

纯碎演示循环依赖问题,不是核心代码。

public interface IQueryService {
   
   
   public String query(String name);
}
@DemoService
public class QueryService implements IQueryService {

    @DemoAutowired
    private IModifyService modifyService;

    
    public String query(String name) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = sdf.format(new Date());
        String json = "{name:"" + name + "",time:"" + time + ""}";
        return json;
    }

}
public interface IModifyService {

   
   public String add(String name, String addr);
   
   
   public String edit(Integer id, String name);
   
   
   public String remove(Integer id);
   
}
@DemoService
public class ModifyService implements IModifyService {

    @DemoAutowired
    private IQueryService queryService;

    @DemoAutowired
    private IAddService addService;

    
    public String add(String name, String addr) {
        return "modifyService add,name=" + name + ",addr=" + addr;
    }

    
    public String edit(Integer id, String name) {
        return "modifyService edit,id=" + id + ",name=" + name;
    }

    
    public String remove(Integer id) {
        return "modifyService id=" + id;
    }

}
@DemoController
@DemoRequestMapping("/web")
public class MyAction {

    @DemoAutowired
    IQueryService queryService;
    @DemoAutowired
    IModifyService modifyService;

    @DemoRequestMapping("/query.json")
    public void query(HttpServletRequest request, HttpServletResponse response,
                      @DemoRequestParam("name") String name) {
        String result = queryService.query(name);
        out(response, result);
    }

    @DemoRequestMapping("/add*.json")
    public void add(HttpServletRequest request, HttpServletResponse response,
                    @DemoRequestParam("name") String name, @DemoRequestParam("addr") String addr) {
        String result = modifyService.add(name, addr);
        out(response, result);
    }

    @DemoRequestMapping("/remove.json")
    public void remove(HttpServletRequest request, HttpServletResponse response,
                       @DemoRequestParam("id") Integer id) {
        String result = modifyService.remove(id);
        out(response, result);
    }

    @DemoRequestMapping("/edit.json")
    public void edit(HttpServletRequest request, HttpServletResponse response,
                     @DemoRequestParam("id") Integer id,
                     @DemoRequestParam("name") String name) {
        String result = modifyService.edit(id, name);
        out(response, result);
    }


    private void out(HttpServletResponse resp, String str) {
        try {
            resp.getWriter().write(str);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

Jetty - Debug 测试


出现这种问题的原因在于IOC容器对Bean初始化是根据BeanDefinition循环迭代,有一定的顺序。这样在执行依赖注入的时候,需要自动赋值的对象有可能还没初始化,所以导致没有实例可以注入。

如何解决循环依赖? 流程
  • 获取对象是从getBean()开始

  • 加一个容器,只要是相互依赖关系的则标记以下

  • 把已经创建好的对象放到一个新的缓存里面(没有DI,没有自动复制);一级缓存

    IQueryService

    ​ — IModifyService

    递归调用 getBean(“ModifyService”)

  • 在创建新的Bean之前,加一个循环依赖的判断

    首先去一级缓存里面检查

  • 依赖注入的方法为之前的populateBean()方法

    factoryBeanInstanceCache.get(beanName) ;终极缓存

三级缓存

实际上解决循环依赖问题,一级缓存就足够了。

一级缓存
已经完成依赖注入的Bean、所有成熟的Bean

二级缓存
早期的纯净Bean

三级缓存
为以后的AOP实现动态代理做准备

不支持循环依赖的场景
  • 通过构造器注入实例不能支持循环依赖
  • 非单例模式的,不支持循环依赖
处理循环依赖

处理循环依赖最根本的解决方法在于递归调用getBean方法。

  • 首先创建相关缓存信息。

    //循环依赖的标识,当前正在创建的BeanName,Mark一下
    private Set singletonCurrentlyInCreation = new HashSet<>();
    
    //一级缓存
    private Map singletonObjects = new HashMap<>();
    
    //二级缓存 早期的纯净bean
    private Map earlySingletonObjects = new HashMap<>();
    
  • 循环依赖的入口

    private Object getSingleton(String beanName, DemoBeanDefinition beanDefinition) {
    
        // 先去一级缓存获取数据
        Object bean = singletonObjects.get(beanName);
    
        //如果一级缓存中没有,但是又有创建标识,说明就是循环依赖
        if (bean == null && singletonCurrentlyInCreation.contains(beanName)) {
            bean = earlySingletonObjects.get(beanName);
    
            //如果二级缓存也没有,则从三级缓存中获取bean
            if (bean == null) {
                Object object = instantiateBean(beanName, beanDefinition);
    
                //将创建出来的对象重新放入到二级缓存中,避免重复创建
                earlySingletonObjects.put(beanName, object);
            }
        }
        return bean;
    }
    
  • 修改 getBean 方法

    public Object getBean(String beanName) {
        //1.先拿到BeanDefinition配置信息
        DemoBeanDefinition beanDefinition = registry.beanDefinitionMap.get(beanName);
    
        //循环依赖 入口
        Object singleton = getSingleton(beanName, beanDefinition);
        if (singleton != null) {
            return singleton;
        }
    
        //标记bean正在创建
        if (!singletonCurrentlyInCreation.contains(beanName)) {
            singletonCurrentlyInCreation.add(beanName);
        }
    
        //2.反射实例化对象
        Object instance = instantiateBean(beanName, beanDefinition);
    
        //先存到一级缓存
        this.singletonObjects.put(beanName, instance);
    
        //3.将返回的bean对象 封装成 BeanWrapper
        DemoBeanWrapper beanWrapper = new DemoBeanWrapper(instance);
    
        //4.执行依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);
    
        //5.保存到IOC容器中
        this.factoryBeanInstanceCache.put(beanName, beanWrapper);
    
        return beanWrapper.getWrappedInstance();
    }
    
测试

修改完之后,重新debug便可以发现循环依赖问题已经解决,实例都已成功注入。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4659210.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-06
下一篇 2022-11-06

发表评论

登录后才能评论

评论列表(0条)

保存