一.前言
hello,everyone。接着上篇文章求求你,别写祖传代码了,继续聊聊架构与代码设计的问题。
相信大家在开发的过程中一定遇到过
- maven依赖冲突
- 工具类重复定义
- 不同的工程中反复定义重复的配置类
- 不同的工程中包层级划分不一致
- ....
本文将给大家介绍一种中小型公司开发微服务时比较合适的一种架构模式,如有不对之处,欢迎指出,共同进步~
二.maven依赖管理
大多数的小公司,甚至一些中型公司都是没有专门的基础架构部门的。各个业务线开发过程中对于各种框架【maven引入】使用也是乱七八糟。博主所在的公司对于工程依赖的安全性管理要求很高,有专门的安全测试部门对生产环境的布置的jar包进行安全测试。其中有一个核心关注的安全测试点就是是否引入了有安全漏洞的maven依赖。
像之前市面上最受欢迎的阿里序列化框架fastjson,频繁爆出严重漏洞,安全测试人员要求我们所有生产环境的jar包必须升级到安全版本或者移除。考虑到移除工具可能会引入大量的代码修改工作,自然就选择了版本升级的解决方式。但是这种方式的麻烦点在于,需要一个个业务包修改。如果发现了其他的安全漏洞依赖,也需要对其进行修改。虽然不是什么高难度棘手的问题,但是一个个工程的修改,工作就很重复,我有这时间,多摸一会儿鱼,他不香吗。
因此,建立一个统一的版本依赖体系是尤其重要的。
2.1.maven依赖优先级
最短路径优先
一个项目Demo依赖了两个jar包,其中A-B-C-X(1.0) , A-D-X(2.0)。由于X(2.0)路径最短,所以项目使用的是X(2.0)。
pom申明顺序优先
代码语言:javascript复制 如果A-B-X(1.0) ,A-C-X(2.0) 这样的路径长度一样怎么办呢?这样的情况下,maven会根据pom文件声明的顺序加载,如果先声明了B,后声明了C,那就最后的依赖就会是X(1.0)。
2.2.版本控制
maven中的版本控制依赖dependencyManagement标签
例如
代码语言:javascript复制<dependencyManagement>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.43</version>
</dependency>
</dependencyManagement>
这样在实际dependencies标签中使用的时候就可以直接忽略版本号的定义
代码语言:javascript复制<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
2.3.最高父级版本工程
2.3.1.单体项目全局版本管理
父pom
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.10.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.baiyan</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<packaging>pom</packaging>
<modelVersion>4.0.0</modelVersion>
<description>Demo</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<spring-cloud-alibaba-dependencies.version>2.2.3.RELEASE</spring-cloud-alibaba-dependencies.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba-dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
子模块中pom依赖直接使用
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.baiyan</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>demo-start</artifactId>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
//省略过多依赖
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
通过2.1与2.2的知识,我们结合到多模块的应用中。我们可以在父pom中定义dependencyManagement,在子pom中需要用到依赖的地方直接引入即可。在单体项目内做到了全局的版本管理。
2.3.2.多项目全局管理
OK,看到这里我们还是没看到到底怎么样才能做到所有项目通用依赖都统一管理。
我们看一下父pom中denpendcies里面对spring-boot-starter-web模块的引用,并没有写上版本号。发现在
代码语言:javascript复制<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.10.RELEASE</version>
<relativePath/>
</parent>
内部定义了各种spring相关或者一些通用的版本管理定义。
接着这个思想,我们也来新建一个空的工程,专门用来管理maven版本
代码语言:javascript复制├── pom.xml
└── baiyan-common-dependency.iml
具体操作
1.pom的父pom依赖spring-boot
2.pom内定义通用的一些maven依赖版本
3.推送该工程至中央仓库
4.业务应用将父pom从spring-boot切换为baiyan-common-dependency
这样类似于一些springboot的版本,fastjson的版本等等比较具有通用性质的依赖版本定义都可以放在单独的依赖管理工程中,一旦依赖出现安全问题,只要一处地方修改就可以。
当然你也不用担心这种情况:
比如你可能想用springboot的版本是2.3.8.RELEASE,但是在依赖工程中的版本已经变成2.3.10.RELEASE。那么根据最短路径依赖优先级你只要在业务工程管理版本出指定对应的springboot版本即可,灵活性也是港港的。
三.模块划分
业务模块划分这个上面没有一个严格的业界标准。也没有说一定要按照怎么设计。微服务架构体系下,以openFeign作为rpc框架的应用,我建议包划分为以下几个模块
- api:定义微服务提供者的接口定义,将openFeign相关的接口定义,所必须的交互实体,枚举等定义此处
- base:与业务无关的工具类与通用配置管理
- rpc:api包的openFeign定义实现,这里如果嫌麻烦api包跟rpc包可以合并,我习惯分开,接口结构更加清晰
- service:业务处理,这个包比较大,里面的逻辑会比较多
- interaction:用户交互层,controller,MqListener,openFeign接口实现等本应用与外部交互的方法定义
- start:启动包,配置文件定义,日志管理定义等全局处理配置定义
模块这样划分之后,其实相对而言结构已经比较清晰了,但是在service模块还是一个比较庞大的包。在service模块内部还要继续进行划分,原则为一类架构功能为一个文件夹,例如所有的配置相关的定义,放在config包中。
四.工具类/配置类剥离
在第三点之后,你的项目结构应该是比较清晰的了。但是你应该还是会有一种感觉工具类,mybatis-plus的分页配置,swagger配置等这种配置在重复的定义在各种项目中。
老规矩,继续新建一个common工程,按照第三点的模块划分。将通用工具类与通用的配置类定义在common工程中。通用的配置通过spring-spi机制提供starter功能。然后对应的业务应用分别引用对应的common模块即可,简化了工具类与配置类的重复定义。
五.总结
最后的依赖关系就变成了业务应用依赖common包pom与配置,common包pom依赖common-dependency
六.源码获取
Common工程的demo代码已经在我的github上放置.
感兴趣的大家可以down下来看看:https://github.com/louyanfeng25/common-frame