前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >架构师技能7:循环依赖引发的架构设计思考

架构师技能7:循环依赖引发的架构设计思考

作者头像
黄规速
发布2022-05-11 14:24:34
7560
发布2022-05-11 14:24:34
举报
文章被收录于专栏:架构师成长之路

本文单纯想把之前的文章链接做总结。

一、背景

1、问题

最近团队项目一个服务出现循环依赖的问题,导致无法启动。

循环依赖即:bean A依赖于另一个bean B,而bean B又依赖于bean A,这个时候就很容易形成一个闭环甚至死循环下去。

官网:Core Technologies

spring造成循环依赖也是有前提条件:Autowired构造器注入和prototype类型的field注入。

而单例的属性注入是可以成功的。

Autowired构造注入:

代码语言:javascript
复制
@Component
public class BServiceImpl implements IBService {


    private AService aService;

    @Autowired
    public BServiceImpl(AService aService) {
        this.aService = aService;
    }
}

@Component
public class AServiceImpl implements IAService {


    private BService bService;

    @Autowired
    public AServiceImpl(BService bService) {
        this.bService = bService;
    }
}

prototype类型的field注入:

@Scope(“prototype”) spring中bean的scope属性,有如下5种类型: singleton 表示在spring容器中的单例,通过spring容器获得该bean时总是返回唯一的实例 prototype表示每次获得bean都会生成一个新的对象 request表示在一次http请求内有效(只适用于web应用) session表示在一个用户会话内有效(只适用于web应用) globalSession表示在全局会话内有效(只适用于web应用) 在多数情况,我们只会使用singleton和prototype两种scope,如果在spring配置文件内未指定scope属性,默认为singleton。

2、直接的曲线解决办法:

1、使用@Lazy延迟加载bean

打破循环的一个简单方法是让Spring延迟地初始化其中一个bean。那就是:它不是完全初始化bean,而是创建一个代理将它注入另一个bean。注入的bean只有在第一次需要时才会完全创建。

2、使用Setter注入

最流行的解决方法之一,也是Spring文档提出的,是使用setter注入。

3、使用@PostConstruct来

在其中一个bean上使用@Autowired注入依赖项,然后使用@PostConstruct注释的方法来设置其他依赖项。

4、实现ApplicationContextAwareInitializingBean

实现ApplicationContextAware_InitializingBean,然后通过_BeanUtil类获取另外的bean:

代码语言:javascript
复制
BeanUtil.getBean(xxxService.class)

如果想追根究底的话,可以学习关于循环依赖底层原理:

Spring Boot(6) 原理和启动流程_hguisu的博客-CSDN博客

Spring学习笔记(2)一DI依赖注入和Spring Bean配置、注解原理、动态注入

二、问题的思考:工程代码架构规范

如果出现循环依赖的问题,虽然可以通过上面的直接曲线方法来解决,可以肯定的是应用程序设计以下问题之一:

1、类职责设计问题。

2、接口依赖设计原则问题。

3、顶层结构设计的问题。

我们遇到的问题,xxxService依赖xxxUtils, 按理说xxxUtils应该是静态方法类,结果是spring bean,这是我们历史遗留的问题:

1、类职责不清:xxxUtils职责应该都是静态方法类,不应该是spring bean。

2、接口依赖设计原则:xxxService和xxxUtils直接注入具体实现对象,违反依赖倒置设计原则,针对接口编程。

3、顶层结构设计的问题:如果xxxUtils确实要做成spring bean,可以下沉到通用基础层如manager层,但是我们这个应用没有任何通用基础层。

1、顶层结构设计:

在之前的博文已经详细说明

架构师技能1:Java工程规范、浅析领域模型VO、DTO、DO、PO、优秀命名

 如果应用程序层次设计规范,层次调用清晰,就不会出现这个循环依赖的问题。

2、接口依赖设计原则:

面向对象设计三大核心思想原则:封装变化点,对接口进行编程,多使用组合而不是继承。

面向对象设计常用的7个原则也基本从上面三大核心原则衍生出来,这些原则也并不是孤立存在的,它们相互依赖,相互补充。前5个原则组合称为:SOLID 固定原则

设计原则:面向对象设计原则详解_hguisu的博客-CSDN博客_面向对象设计原则

三、问题的思考:架构设计

面对大工程,仍然需要有一定的方法论,架构设计的本质是管理复杂性,因此做好架构设计就避免一些不应该出现的问题,如上面提到的循环依赖问题。若大项目工程没有沉淀下相关架构设计文档之类,那该工程后续的扩展大概率朝着技术体系失控的方向发展。

我个人比较习惯写架构设计文档,最后写下项目总结。善于总结、不断反思做更好的自己_hguisu的博客-CSDN博客_善于总结反思

架构设计设计文档的目的:

架构设计(1)-谈谈架构_hguisu的博客-CSDN博客_架构

架构设计(7)—如何设计架构和画架构图_

总结的目的是复盘当前项目是否和当初的架构设计相符。

例如针对我们最近项目做了个总结:

总结的主要内容:设计哪些方面?在此过程中应用哪些原则,服务化如何做,组件化如何实施,计划是否顺利实施?在此过程中遇到哪些问题?

1、总体架构

2、指导原则:

主要总结项目应用来哪些原则:架构设计不像数据公式或者定律,很难一概而就。很多时候是设计者(架构师)的各种设想,各种权衡折中而符合系统需求的智慧输出。一些好的架构设计原则可以确保设计决策在一定程度上能够满足需求。

比如典型的分布式事务场景,上层应用服务A,完整的事物逻辑如下

代码语言:javascript
复制
事务开启
  任务1:改数据A // sql 存储
  任务2:调用系统底层服务接口C // -- 被调服务可能是类似情况
  任务3:调用外部服务接口D
提交事务

由于外部服务接口D不可控,可能会导致事务提交失败。因此上层服务A需要有幂等设计,那么底层服务接口C也有有幂等能力,确保上层服务A可以幂等调用。这就是在做设计的时候要求服务符合业务原则的幂等设计原则和应用设计的无状态原则。

具体可以查看:架构设计(2)-架构设计原则

 3、组件化设计

总结项目使用哪些组件,这些组件的层次结构如何设计:

组件化就是基于可重用的目的,将一个大的软件系统按照分离关注点的形式,拆分成多个独立的组件,已较少耦合。把重复的代码提取出来合并成为一个个组件,组件最重要的就是重用(复用),位于框架最底层,其他功能都依赖于组件,可供不同功能使用,独立性强。

大部分来说,组件主要分三层:业务组件,基础业务组件以及基础组件,组件之间只能通过接口耦合,也就是依赖倒置原则,每个组件都提供对外的接口文档以描述该组件提供的功能。

架构师技能2:组件化思想之框架、脚手架、基础应用框架。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景
    • 1、问题
      • 2、直接的曲线解决办法:
        • 1、使用@Lazy延迟加载bean
        • 2、使用Setter注入
        • 3、使用@PostConstruct来
        • 4、实现ApplicationContextAware和InitializingBean
    • 二、问题的思考:工程代码架构规范
      • 1、顶层结构设计:
        • 2、接口依赖设计原则:
        • 三、问题的思考:架构设计
          • 1、总体架构
            • 2、指导原则:
              •  3、组件化设计
              相关产品与服务
              容器服务
              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档