随着项目的不断发展,需求和业务的不断细化与添加,工程代码会越来越庞大,包结构也越来越复杂,不同方面的代码之间相互耦合,杂乱而无章,并且开发人员也都同时在这一个项目里修改,合并代码时必然会出现各种各样的问题,而且当一位新的人员参与进项目,很难对项目有一个直观的感受,这间接的导致了开发效率的下降。
多模块化,正式解决上述问题而产生的,总的来说模块化开发有以下几点好处:
本文以springboot集成nacos的注册中心和feign为例,编译器用的Intellij IDEA,Java版本是1.8。
其实我用模块化开发,主要原因是微服务之间用feign互相调用接口的时候,发现实体类的参数没法传递啊,传个map感觉又不太雅观不正规,所以就想到了这个办法,把公用的实体类放到pom里面,直接搞成模块化
File—>New—>Project
最外层的父模块,无须启动类,直接创建maven工程
自定义创建好了,直接Next,Next,Finish完事!
下面看一下父模块pom结构,因为之前已经搭建好了一个,就直接用这个说了,下面直接上代码,所有内容都在代码的注释里,
<?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">
<!--声明项目描述符遵循哪一个POM模型版本。模型本身的版本很少改变,虽然如此,但它仍然是必不可少的,这是为了当Maven引入了新的特性或者其他模型变更的时候,确保稳定性。 -->
<modelVersion>4.0.0</modelVersion>
<!--父项目的坐标。如果项目中没有规定某个元素的值,那么父项目中的对应值即为项目的默认值。 坐标包括group ID,artifact ID和
version。 -->
<!--父模块为springboot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
</parent>
<!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.companyname.project-group,maven会将该项目打成的jar包放本地路径:/com/companyname/project-group -->
<!--项目的全球唯一标识符,通常使用全限定的包名区分该项目和其他项目。并且构建时生成的路径也是由此生成, 如com.mycompany.app生成的相对路径为:/com/mycompany/app -->
<groupId>com.shopping.nacos</groupId>
<!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
<!-- 构件的标识符,它和group ID一起唯一标识一个构件。换句话说,你不能有两个不同的项目拥有同样的artifact ID和groupID;在某个
特定的group ID下,artifact ID也必须是唯一的。构件是项目产生的或使用的一个东西,Maven为项目产生的构件包括:JARs,源 码,二进制发布和WARs等。 -->
<artifactId>shopping</artifactId>
<!-- 版本号 -->
<version>1.0-SNAPSHOT</version>
<!--打包方式,项目产生的构件类型,例如jar、war、ear、pom。插件可以创建他们自己的构件类型,所以前面列的不是全部构件类型 -->
<packaging>pom</packaging>
<!--统一管理版本号-->
<properties>
<java.version>1.8</java.version>
<spingboot.version>2.3.1.RELEASE</spingboot.version>
<cloud.version>Greenwich.SR2</cloud.version>
<cloud.alibaba.version>0.2.2.RELEASE</cloud.alibaba.version>
<mybatis-plus.version>3.1.0</mybatis-plus.version>
</properties>
<!--引入的下一层级的所有子模块-->
<modules>
</modules>
<!--<dependencyManagement>主要管理版本,对于子类继承同一个父类是很有用的,集中管理依赖版本不添加依赖关系,对于其中定义的版本,子pom不一定要继承父pom所定义的版本。-->
<!--指定cloud的版本和nacos的版本-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${
cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${
cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--引入的依赖,这里引入了,子模块就默认继承了父模块的依赖-->
<dependencies>
<!--公用依赖:springboot web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--公用依赖:springboot test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--<!–lombok–>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--StringUtils工具包-->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
<!--这里是打包时候的设置,如果有入口类,还需要加一个入口类的设置.还有其他问题什么的,自行百度吧-->
<build>
<plugins>
<!--这是因为测试代码时遇到错误,它会停止编译。只需要在pom.xml的<project>里添加以下配置,使得测试出错不影响项目的编译。-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
</plugins>
</build>
</project>
pom可以分为几块去看
公用子模块,顾名思义,就是都能用到的功能,抽离出来,然后谁用,就在Pom里引入就行了。
比如下面这个例子中,mybatis的分页插件,我单独抽出来了,其实好像并没有什么必要,一是因为举例说明,二是如果以后又什么新功能配置加入,好做横向扩展,到时候就看出作用。
公用的pojo我也抽离出来了,这的作用主要是一些微服务之间共享的实体类
下面我们从父模块下面创建公用的子模块
公用子模块就是用来放公用的接口,方法,配置,还有实体类等等
因为这个模块是公用的模块,比如放一些基本配置啊,pojo实体类啊什么的,所以这里不用启动类,直接创建个maven工程就可以了,基本流程还是和上面一样,选择Module,选择Maven,创建就完事
这是创建好的公用子模块,里面还要放各种公用的模块,依然把src删了。
配置一下这个公用子模块的Pom
<?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>
<artifactId>shopping</artifactId>
<groupId>com.shopping.nacos</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<!--项目的唯一ID,就是刚才创建时候的名字-->
<artifactId>shopping-common</artifactId>
<!--因为这里下面还有子模块,这里的打包方式也要用pom-->
<packaging>pom</packaging>
<description>公用模块</description>
<!--下面的子模块-->
<modules>
</modules>
</project>
然后再以相同的步骤,在这个子模块里,再创建子模块,依然是创建maven工程。这个嵌套的子模块,就是具体的公用功能了
不同的是src目录别删,因为要写具体的功能
在java文件夹下,创建目录
首先把java文件夹变成蓝色文件夹,Sources类型,这样才能在下面创建java class
创建目录结构,之所以这样创建,是因为整个项目总体需要统一起来,当引入其他模块的时候,其他模块的启动类才可以把这里的启动配置加载进去
创建好了Mybatisplus的分页插件
下面贴出mybatis子模块的pom
<?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>
<artifactId>shopping-common</artifactId>
<groupId>com.cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shopping-common-mybatis</artifactId>
<!--这里具体到功能了,需要打成jar包了-->
<packaging>jar</packaging>
<description>mybatis公用类</description>
<dependencies>
<!--=========mybatis plus======================-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${
mybatis-plus.version}</version>
</dependency>
<!--<!–<!–=========mysql======================–>–>-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
注意,这里的packaging要设置成jar了,因为具体到功能模块了
公用子模块的创建就到这了,其他需要啥公用的功能,往里加就完事了
下面再创建一个pojo公用实体类的模块
前面创建步骤和上面一样
这里需要引入刚才创建的mybatisplus模块,因为实体类需要mybatisplus的@TableName等注解
还有什么其他的公用的模块,比如日志,权限,工具类等等公用的模块都可以像上述模块一样往里加,下面将功能模块的创建,以及如何调用这些公用的模块
功能模块就是具体到业务里的某个功能了,作为微服务,是需要有启动类的,所以需要创建springboot项目,并且打包方式为jar
首先我们创建一个模块,暂且就定义为订单服务吧
创建流程
首先要做的是
注意,是删掉这个文件夹,把启动类,复制到nacos文件夹下。因为所有模块的启动类,这里尽量都要保证在com.shopping.nacos下,因为这样启动的时候才会正确加载启动所有要引入的配置项。往后的模块也需要这样做,都统一起来
下面我们来修改一下pom
<?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">
<modelVersion>4.0.0</modelVersion>
<!--指定父依赖-->
<parent>
<groupId>com.shopping.nacos</groupId>
<artifactId>shopping</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.shopping.nacos</groupId>
<artifactId>shopping-order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shopping-order</name>
<packaging>jar</packaging>
<description>订单信息微服务</description>
<dependencies>
<!--引入自定义mybatis插件模块-->
<dependency>
<groupId>com.shopping.nacos</groupId>
<artifactId>shopping-common-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--引入alibaba nacos注册中心依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.shopping.nacos</groupId>
<artifactId>shopping-common-pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--Redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.8.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--这里引入了spring-boot-maven-plugin,打包时会去扫描项目main方法入口,也就是说引入该配置,你就必须在项目src/main/java/下创建一个spring-boot启动类:-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改了parent为自己的父类,packaging打包方式为jar,还有把指定版本的properties删掉,因为这些都要写在父依赖的pom里。下面dependencies里面需要引入本模块需要的依赖。
注意这里引入了上面自己的Mybatis和Pojo,这样确实方便和舒适很多。
然后,找到最外层父依赖的pom,把这个模块加进去
很清楚了吧兄弟
下面,来配置订单服务模块的配置文件
server:
port: 8001
spring:
application:
#服务名,微服务用此名字注册服务并发现服务
name: shopping-order
cloud:
nacos:
discovery:
#nacos服务注册与发现地址
server-addr: IP:8848
#数据源配置
datasource:
username: root
password: 密码
url: jdbc:mysql://IP:3306/shopping_mall?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.jdbc.Driver
#Redis配置
redis:
database: 0
host: IP
port: 6379
password: 密码
timeout: 1000
mybatis-plus:
# 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
mapper-locations: classpath:mybatis/*Mapper.xml
typeAliasesPackage: com.shoppingmall.wares.pojo
global-config:
db-config:
#id-type: uuid
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 1
#驼峰下划线转换
#db-column-underline: true
#刷新mapper 调试神器
#refresh-mapper: true
#数据库大写下划线转换
#capital-mode: true
# Sequence序列接口实现类配置
#key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
#逻辑删除配置(下面3个配置)
#logic-delete-value: 1
#logic-not-delete-value: 0
#sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
#自定义填充策略接口实现
#meta-object-handler: com.baomidou.springboot.MyMetaObjectHandler
configuration:
#配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
#true:开启数据库下划线对应实体类转驼峰;false:不开启
map-underscore-to-camel-case: true
cache-enabled: false
#配置JdbcTypeForNull, oracle数据库必须配置
jdbc-type-for-null: 'null'
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
database-id: mysql
注意name: shopping-order这个服务名字很重要,服务之间通信全靠他了。
下面,启动类加入@EnableDiscoveryClient,开启Naocs服务与发现注册中心
然后创建个controller,写个post请求的接口
启动订单模块,看nacos控制台,服务列表,已经有shopping-order这个服务了
下面我们创建一个消费服务的库存模块
和上面的步骤一样,我就不重复贴了。这里测试一下,库存模块来消费订单模块的接口,所以加入了feign。
<!--引入alibaba nacos-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.shopping.nacos</groupId>
<artifactId>shopping-common-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.shopping.nacos</groupId>
<artifactId>shopping-common-pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
这是需要引入的依赖
启动类上加入2个注解,一个是nacos注册中心服务发现的注解@EnableDiscoveryClient,另一个是feign的注解@EnableFeignClients
配置文件,贴代码
server:
port: 8002
spring:
application:
name: shopping-order
cloud:
nacos:
discovery:
server-addr: IP:8848
datasource:
username: root
password: zaqxsw
url: jdbc:mysql://IP:3306/shopping_mall?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.jdbc.Driver
mybatis-plus:
mapper-locations: classpath:mybatis/*Mapper.xml
typeAliasesPackage: com.shoppingmall.wares.pojo
global-config:
db-config:
field-strategy: 1
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
jdbc-type-for-null: 'null'
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
database-id: mysql
#fengin请求超时时间,单位毫秒
ribbon:
ReadTimeout: 5000
ConnectTimeout: 5000
@FeignClient(“shopping-order”)注解,表明了这是个feign类,括号里是调用的服务名字,下面的写的接口,是调用"shopping-order"服务暴露的接口方法,对照上面的订单服务
然后在controller层去调用这个接口,去测试一下,feign的接口是不需要实现的
controller层
启动库存服务,看一下nacos操作平台,库存服务也注册到服务发现的列表里去了
然后用postman测试一下,库存服务的端口是8002
好了,feign消费微服务成功!
下面我们将这这2个服务打包,放到不同的服务器上
订单服务放在201服务器上,库存服务放在202服务器上
OK,完事。
如果服务之间的feign接口,有实体类参数的话,直接pom里引入公用的pojo实体类,这么干,也可以搞定了。