Spring 与 SpringMVC 整合 Dubbo 2 案例

大纲

前言

学习资源

版本对应关系

Spring 与 Dubbo 的版本必须互相匹配,否则不同版本之间可能会存在兼容性问题,最终导致服务无法正常运行。两者的版本对应关系如下:

Dubbo 版本适配 Spring 版本核心特性 / 注意事项
‌Dubbo 3.x‌ (3.3.0+)✅ Spring 6.x
✅ Spring 5.3+
(1) 原生支持 Spring 6 注解驱动
(2) 依赖 Jakarta EE 9+(需 JDK 17+)
(3) 默认集成 Spring Boot 3.x
‌Dubbo 3.0.x‌ (3.0.0 - 3.2.x)✅ Spring 5.3+
⚠️ Spring 6.x(需手动适配)
(1) 需手动排除旧版 Jakarta API(jakarta.*
(2) 建议搭配 Spring Boot 2.7+
‌Dubbo 2.7.x✅ Spring 5.2+
❌ Spring 6.x
(1) 仅支持 Java EE(javax.*
(2) 官方已停止维护,仅建议旧项目使用

Dubbo 整合案例

本节将整合 Spring、SpringMVC 与 Dubbo,并使用 ZooKeeper 作为 Dubbo 的注册中心。

项目结构

版本说明

组件版本说明
Spring5.2.25.RELEASE
Dubbo2.7.23
Zookeeper Server3.5.7ZooKeeper 服务器,作为服务注册中心
Curator Recipes5.5.0用于 Dubbo 连接 ZooKeeper 注册中心,依赖 Curator Client 5.5.0 和 ZooKeeper Client 3.7.1
JDK1.8支持 JDK 1.8 及以上版本

模块说明

  • api:抽取出来的公共模块,存放公用的实体类和接口
  • provider:服务提供者,实现 api 模块中的接口
  • customer:服务消费者,调用服务提供者中的接口

案例代码

API 模块

  • 为了简化代码,API 模块只简单定义一个接口
1
2
3
4
5
public interface UserService {

String sayHello(String name);

}

Provider 模块

  • 引入依赖,并添加 Maven 的 Tomcat 7 插件,方便一键将应用部署到 Tomcat 7 中运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<properties>
<javax.servlet.version>3.1.0</javax.servlet.version>
<spring.version>5.2.25.RELEASE</spring.version>
<dubbo.version>2.7.23</dubbo.version>
<curator.version>5.5.0</curator.version>
<commons.version>3.4</commons.version>
<slf4j.version>1.7.21</slf4j.version>
</properties>

<dependencies>
<!--API-->
<dependency>
<groupId>com.clay.dubbo</groupId>
<artifactId>dubbo-lesson-05-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--SpringMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${javax.servlet.version}</version>
<scope>provided</scope>
</dependency>
<!--Dubbo-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!--Curator-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator.version}</version>
</dependency>
<!--Commons-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<!--Tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>9000</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
  • 接口实现类,@DubboService 注解主要用于暴露服务,使其能够被 Dubbo 框架识别并注册到服务注册中心
1
2
3
4
5
6
7
8
9
10
11
import org.apache.dubbo.config.annotation.DubboService;

@DubboService
public class UserServiceImpl implements UserService {

@Override
public String sayHello(String name) {
return "Hello " + name;
}

}
  • Spring 的 XML 配置文件(application-context.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!-- 扫描并注册 Spring 组件 -->
<context:component-scan base-package="com.clay.dubbo.producer"/>

<!-- 配置服务应用名 -->
<dubbo:application name="dubbo-provider-application">
<dubbo:parameter key="qos.enable" value="true"/>
<dubbo:parameter key="qos.port" value="22222"/>
<dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
</dubbo:application>

<!-- 注册中心配置 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="5000"/>

<!-- 元数据中心配置 -->
<dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>

<!-- 配置服务协议 -->
<dubbo:protocol name="dubbo" port="20880"/>

<!-- 开启 Dubbo 的注解扫描 -->
<dubbo:annotation package="com.clay.dubbo.producer"/>

</beans>
  • SpringMVC 的 XML 配置文件(application-mvc.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!-- 启用 SpringMVC 的注解驱动功能 -->
<mvc:annotation-driven/>

<!-- 扫描 Web 控制器 -->
<context:component-scan base-package="com.clay.dubbo.producer.controller"/>

</beans>
  • Java EE 的 XML 配置文件(web.xml),存放在项目中的 /src/main/webapp/WEB-INF 目录下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/Java EE"
xsi:schemaLocation="http://java.sun.com/xml/ns/Java EE http://java.sun.com/xml/ns/Java EE/web-app_2_5.xsd"
version="2.5">

<!-- Spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/application-context.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- SpringMVC -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/application-mvc.xml</param-value>
</init-param>
</servlet>

<!-- Servlet -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
  • Log4j 的配置文件(log4j.properties
1
2
3
4
5
6
7
8
log4j.rootLogger=INFO, stdout, file
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=log/app.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n

Consumer 模块

  • 引入依赖,并添加 Maven 的 Tomcat 7 插件,方便一键将应用部署到 Tomcat 7 中运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<properties>
<javax.servlet.version>3.1.0</javax.servlet.version>
<spring.version>5.2.25.RELEASE</spring.version>
<dubbo.version>2.7.23</dubbo.version>
<curator.version>5.5.0</curator.version>
<commons.version>3.4</commons.version>
<slf4j.version>1.7.21</slf4j.version>
</properties>

<dependencies>
<!--API-->
<dependency>
<groupId>com.clay.dubbo</groupId>
<artifactId>dubbo-lesson-05-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--SpringMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${javax.servlet.version}</version>
<scope>provided</scope>
</dependency>
<!--Dubbo-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!--Curator-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator.version}</version>
</dependency>
<!--Commons-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<!--Tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8000</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
  • 业务测试类,@DubboReference 注解主要用于在服务消费者端引用远程服务提供者的服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.apache.dubbo.config.annotation.DubboReference;

@RestController
@RequestMapping("/user")
public class UserController {

@DubboReference
private UserService userService;

@GetMapping("/sayHello/{name}")
public String sayHello(@PathVariable("name") String name) {
return userService.sayHello(name);
}

}
  • Spring 的 XML 配置文件(application-context.xml
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!-- 扫描并注册 Spring 组件 -->
<context:component-scan base-package="com.clay.dubbo.consumer"/>

</beans>
  • SpringMVC 的 XML 配置文件(application-mvc.xml

特别注意

在服务消费者模块中,必须先扫描 Dubbo 的注解(如 @DubboReference,为了引用远程服务),然后再扫描 Spring 的注解(如 @Controller),否则会出现 @DubboReference 注解失效的问题,详细分析说明请看这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!-- 启用 SpringMVC 的注解驱动功能 -->
<mvc:annotation-driven/>

<!-- 配置服务应用名 -->
<dubbo:application name="dubbo-consumer-application">
<dubbo:parameter key="qos.enable" value="true"/>
<dubbo:parameter key="qos.port" value="22223"/>
<dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
</dubbo:application>

<!-- 注册中心配置 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="5000"/>

<!-- 元数据中心配置 -->
<dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>

<!-- 特别注意,在消费者模块中,必须先扫描 Dubbo 注解,然后再扫描 Web 控制器,否则 Web 控制器中的 @DubboReference 注解将无法实现注入 -->

<!-- 开启 Dubbo 注解扫描-->
<dubbo:annotation package="com.clay.dubbo.consumer"/>

<!-- 扫描 Web 控制器 -->
<context:component-scan base-package="com.clay.dubbo.consumer.controller"/>

</beans>
  • Java EE 的 XML 配置文件(web.xml),存放在项目中的 /src/main/webapp/WEB-INF 目录下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/Java EE"
xsi:schemaLocation="http://java.sun.com/xml/ns/Java EE http://java.sun.com/xml/ns/Java EE/web-app_2_5.xsd"
version="2.5">

<!-- Spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/application-context.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- SpringMVC -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/application-mvc.xml</param-value>
</init-param>
</servlet>

<!-- Servlet -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
  • Log4j 的配置文件(log4j.properties
1
2
3
4
5
6
7
8
log4j.rootLogger=INFO, stdout, file
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=log/app.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n

测试代码

  • (1) 在 IDEA 开发工具内,通过 Tomcat 的 Maven 插件分别按顺序一键运行 Provider 和 Consumer 模块,如下图所示:

  • (2) 使用 Postman 等工具访问 http://127.0.0.1:8000/user/sayHello/dubbo 接口,若接口可以返回 Hello dubbo 结果,则说明 Spring 与 SpringMVC 整合 Dubbo 成功。

下载代码

  • 完整的案例代码可以直接从 GitHub 下载对应章节 dubbo-lesson-05

使用 Nacos 注册中心

  • (1) 由于不再使用 ZooKeeper 作为注册中心,因此需要移除 curator-recipes 依赖
1
2
3
4
5
6
7
<!--
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator.version}</version>
</dependency>
-->
  • (2) 由于需要使用 Nacos 作为注册中心,因此需要引入 dubbo-registry-nacos 依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- Nacos -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>2.7.23</version>
</dependency>

<!-- Nacos Fix -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-common</artifactId>
<version>1.4.3</version>
</dependency>
  • (3) 在 Provider 和 Consumer 模块中,指定 Nacos 注册中心的地址
1
2
3
4
5
<!-- 注册中心配置 -->
<dubbo:registry address="nacos://127.0.0.1:8848" timeout="5000"/>

<!-- 元数据中心配置 -->
<dubbo:registry address="nacos://127.0.0.1:8848" timeout="5000"/>
  • (4) 引入 Nacos 客户端端后,因为不再兼容 Maven 的 Tomcat 7 插件,所以需要注释掉对应的 Maven 配置。另外,在 Maven 仓库中并没有高版本的 Tomcat 插件,因此需要在 IDEA 开发工具内,将各个模块分别手动部署到本地安装的 Tomcat 8+ 服务器中
1
2
3
4
5
6
7
8
9
10
11
<!--
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8000</port>
<path>/</path>
</configuration>
</plugin>
-->