这里写目录标题
- 多模块项目的搭建
- 父项目的构建
- 子模块的创建
- 父子模块的意义
- 将注册服务引入到父子模块中
- 创建子模块用于发现服务和调用
- 供调用的服务接口
- 创建调用子模块
- 测试
- 一些小问题
在前文中我们实现了微服务的注册参考此文:
Spring Cloud Alibaba中Nacos的安装(Windows平台)以及服务的发现
现在我们需要创建另一个项目来发现服务以及调用注册的服务
多模块项目的搭建
为了方便之后的其他组件的引入,我这里就不再创建新的工程
了,而是把之前的注册服务
和接下来的nacos发现服务
都放到同一个工程
里。
也就是从之前的
转为以下这样
父项目的构建
得到的目录结构如图:
然后将src目录
删除,因为父模块不需要运行
,得到以下的目录结构
子模块的创建
然后我们再新建子模块
,右键根目录
,选择new -> module
这里的new module
创建的照样是一个maven工程
,不过此时它存在一个父模块,也就是我们上面的father_project
创建以后的目录这样的
父子模块的意义
大家可能会问了,父子模块的意义
是什么?为什么不能再创建一个项目
?而是使用父子模块
?
在 IntelliJ IDEA
中,父子模块的概念与 Maven 或 Gradle
等构建工具中的模块结构
概念相关联。
父子模块的意义:
-
模块化管理:父子模块的设计
有助于将项目分解为更小、更易管理的模块单元
(听起来像微服务划分的方式)。这种模块化管理使得项目更易于开发和维护
,有助于团队协作和复用。 -
依赖管理:父子模块可以有
依赖关系
,子模块可以依赖于父模块,同时子模块也可以互相依赖。 -
共享配置:
父模块中定义的配置
可以被子模块继承和覆盖
,比如版本管理、插件配置
等。 -
独立构建:子模块可以
独立构建、测试和运行
,同时也可以
依赖于其他子模块,最终打包成整个项目。
将注册服务引入到父子模块中
按照上述的方式,将之前的注册项目
引入到父子模块结构
的项目中,得到如下的项目
这里父项目的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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>SpringCloudAlibabaDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 子模块的信息-->
<modules>
<module>nacos-project-registrant</module>
</modules>
<properties>
<!-- 项目的源代码的Java版本-->
<maven.compiler.source>11</maven.compiler.source>
<!-- 项目编译生成的字节码的目标Java版本-->
<maven.compiler.target>11</maven.compiler.target>
<!-- 项目的源代码文件的字符编码-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- spring boot的版本-->
<spring-boot.version>2.7.4</spring-boot.version>
<!-- spring cloud alibaba的版本-->
<spring-cloud-alibab.version>2021.0.5.0</spring-cloud-alibab.version>
<!-- nacos-discovery的版本-->
<spring-nacos.version>2021.0.5.0</spring-nacos.version>
<!-- spring cloud的版本-->
<spring-cloud.version>2021.0.5</spring-cloud.version>
</properties>
<!-- 需要集中管理的依赖 子模块可以通过父模块引入而不需要自己导入-->
<dependencyManagement>
<dependencies>
<!-- Nacos服务注册和发现的集成-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-nacos.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入spring cloud alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibab.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 部分spring cloud功能使用时候需要引入这个-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 项目的构建配置,包括编译、打包、测试、部署等方面-->
<build>
<plugins>
<!-- spring-boot中用于支持maven打包的插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
子项目的目录结构与之前的相似
这里子模块的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">
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<parent>
<groupId>org.example</groupId>
<artifactId>SpringCloudAlibabaDemo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>nacos-project-registrant</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 由于父模块中已经引入了,所以这里不需要配置版本,项目会自动以父模块为准-->
<dependencies>
<!-- spring boot的引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Nacos服务注册和发现的集成-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<!-- 项目的构建配置,包括编译、打包、测试、部署等方面-->
<build>
<resources>
<!-- 构建项目过程中被复制到输出目录中,以便它们可以在应用程序运行时访问-->
<resource>
<directory>src/main/java</directory>
<includes>
<!-- 项目内mybatis的dao配置文件-->
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<!-- 资源文件-->
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<!-- spring-boot中用于支持maven打包的插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 打包出来的名称 一般是项目的缩写外加版本号 -->
<finalName>npr</finalName>
</build>
</project>
我们再验证一下是否引入成功,启动注册服务
如果nacos
中对应服务存在,则表示成功
如果运行失败,可能是nacos没启动
导致的
创建子模块用于发现服务和调用
接下来我们再创建一个子模块用于发现
注册的服务,以及调用该服务的接口
供调用的服务接口
这里我们先在注册服务
中创建一些接口,供订阅者调用
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class NacosTest {
@GetMapping("/echo/{string}")
public String echo(@PathVariable String string) {
return "成功调用注册服务的接口,代码为:" + string;
}
@GetMapping("/test")
public String test() {
return "调用服务的测试";
}
}
在启动类上添加@EnableDiscoveryClient
注解,启用Nacos客户端的发现
创建调用子模块
按照上面的方式创建一个名为nacos-discoverry-call
子模块,并将其改为spring boot
项目,如下图所示
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">
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<parent>
<groupId>org.example</groupId>
<artifactId>SpringCloudAlibabaDemo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>nacos-discoverry-call</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 由于父模块中已经引入了,所以这里不需要配置版本,项目会自动以父模块为准-->
<dependencies>
<!-- spring boot的引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Nacos服务注册和发现的集成-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- openfeign提供服务的远程调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- open-feign在默认情况下依赖于负载均衡 新版本中已经不再强制依赖ribbon 而是推荐loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
<!-- 项目的构建配置,包括编译、打包、测试、部署等方面-->
<build>
<resources>
<!-- 构建项目过程中被复制到输出目录中,以便它们可以在应用程序运行时访问-->
<resource>
<directory>src/main/java</directory>
<includes>
<!-- 项目内mybatis的dao配置文件-->
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<!-- 资源文件-->
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<!-- spring-boot中用于支持maven打包的插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 打包出来的名称 一般是项目的缩写外加版本号 -->
<finalName>ndc</finalName>
</build>
</project>
application.yml
文件除了application.name以及port
其他无变化:
server:
port: 9001
tomcat:
uri-encoding: UTF-8
spring:
application:
name: nacos-call
cloud:
nacos:
discovery:
server-addr: 192.168.2.102:8848
# 控制应用程序在启动时对配置中心的连接行为 如果没有连接到客户端则会立即启动失败
fail-fast: true
启动类CallApplication
中需要添加以下两个注解,启用服务的发现和调用
:
调用类相关的就以下两个
FeignInterface
调用的接口,定义了某个服务
调用的接口:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
//feign接口提供服务的调用 name为所调用服务的名称
@FeignClient(name="nacos-register")
public interface FeignInterface {
@GetMapping("/echo/{str}")
String echo(@PathVariable("str") String str);
}
NacosTest
为测试的控制器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class NacosTest {
private final static String SERVICE_ADDR = "http://192.168.2.102:9000";
//Fegin接口提供的调用方式
@Autowired
FeignInterface feignInterface;
//直接使用Rest风格模板来实现调用 不推荐
private RestTemplate template = new RestTemplate();
@GetMapping("/echo/{string}")
public String echo(@PathVariable String string) {
//直接使用Rest风格模板来实现调用
return template.getForObject(SERVICE_ADDR + "/echo/" + string , String.class);
}
@GetMapping("/test")
public String test() {
//直接使用Rest风格模板来实现调用
return template.getForObject(SERVICE_ADDR+"/test" , String.class);
}
@GetMapping("/iecho/{string}")
public String iecho(@PathVariable String string) {
//Fegin接口提供的调用方式
return feignInterface.echo(string);
}
}
测试
启动两个子模块,进入Nacos
控制台
这里可以发现两个已经注册的服务,说明没问题,然后我们调用一下,检查是否调用服务(nacos-call)
能否调用到注册服务(nacos-register)
中的接口
地址栏输入http://localhost:9001/iecho/125
调用注册服务中的echo接口
有以下显示即为成功
此时点开订阅者列表
可看到,nacos-call
服务
还可以输入http://localhost:9001/test
测试/test
接口
一些小问题
订阅者有时候不显示应用名称而是显示为unknow
自己给环境设置一下即可
ProjectNameConfig
代码为:
import com.alibaba.nacos.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
//用于设置订阅者一栏中的应用名称
@Configuration
public class ProjectNameConfig implements EnvironmentAware {
@Value("${spring.application.name}")
private String applicationName;
@Override
public void setEnvironment(Environment environment) {
if(StringUtils.isBlank(System.getProperty("project.name"))){
System.setProperty("project.name",applicationName);
}
}
}