基于注解方式的dubbo+spring mvc整合
最近项目使用了dubbo进行服务治理,搭建了一套基于注解方式的dubbo+spring mvc的框架,注册中心使用zookeeper。
关于dubbo的介绍就不多说了,网上有很多可以自行搜索,直接上代码。
项目使用的maven,采用多模块方式。先看一下示例项目框架。
image.png
其中dubbo-config模块管理所有项目所需的配置文件,在打包时打入对应的服务内。
一、项目包依赖 pom.xml
<?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>com.sanjinbest.dubbo</groupId>
<artifactId>sanjinbest.dubbo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>user-service</module>
<module>dubbo-web</module>
<module>dubbo-config</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<servlet.version>3.0.1</servlet.version>
<springframework.version>4.3.7.RELEASE</springframework.version>
<junit.version>4.12</junit.version>
<zookeeper.version>0.2</zookeeper.version>
<dubbo.version>2.5.4</dubbo.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Dubbo相关 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- ZK-client -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.mobile</groupId>
<artifactId>spring-mobile-device</artifactId>
<version>${spring.mobile.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${springframework.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project
二、dubbo服务端
1、服务端配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="user-provider"/>
<!-- 使用zookeeper注册中心暴露服务地址 -->
<dubbo:registry protocol="zookeeper" address="${dubbo.user.registry.server}" />
<!-- 使用dubbo协议,所以请求响应都使用线程池管理 -->
<dubbo:protocol name="dubbo" dispatcher="all" port="${dubbo.user.protocol.port}"/>
<!-- 声明需要暴露的服务接口 -->
<dubbo:annotation/>
<!-- 延迟加载,等待spring初始化完成后暴露服务 -->
<dubbo:provider delay="-1" timeout="${dubbo.user.timeout}" retries="${dubbo.user.retries}" />
</beans>
这里使用的注册中心是zookeeper。
2、服务端applicationContext.xml
<?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
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath*:dubbo.properties</value>
</list>
</property>
</bean>
<!-- dubbo 服务 -->
<import resource="classpath*:dubbo/user-provider.xml"/>
<!-- 开启注解 -->
<context:annotation-config/>
<!-- 自动扫描 -->
<context:component-scan base-package="com.sanjinbest.dubbo.user" />
</beans>
3、服务接口类
package com.sanjinbest.dubbo.user.api;
/**
* <li></li>
*
* @author lixin
* @create 2017/11/3
*/
public interface IUserInfoService {
String getName();
int getAge();
}
这里提供了一个返回name 和 age的方法。当发布服务给第三方的时候,只需要将该接口打成jar包发布即可。
4、接口实现类
package com.sanjinbest.dubbo.user.provider;
import com.sanjinbest.dubbo.user.api.IUserInfoService;
/**
* <li></li>
*
* @author lixin
* @create 2017/11/3
*/
@com.alibaba.dubbo.config.annotation.Service
@org.springframework.stereotype.Service
public class UserInfoServiceProvider implements IUserInfoService{
@Override
public String getName() {
return "sanjinbest";
}
@Override
public int getAge() {
return 29;
}
}
这里有一个需要注意的地方,该实现类内使用了2个service注解:
@com.alibaba.dubbo.config.annotation.Service dubbo服务对外暴露服务
@org.springframework.stereotype.Service spring容器注入
5、dubbo本地测试启动
package mock;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* <li></li>
*
* @author lixin
* @create 2017/9/18
*/
public class UserProviderMock {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath*:spring/applicationContext.xml"});
System.out.println("user provider start...");
context.start();
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
如果是线上使用,可以使用运行jar包的方式运行,下面是打jar包的配置:
<!-- 打包 -->
<build>
<!-- 与服务模块名称一致 -->
<finalName>user-provider</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<classesDirectory>${project.build.directory}/classes/</classesDirectory>
<archive>
<manifest>
<mainClass>com.alibaba.dubbo.container.Main</mainClass>
<!-- 打包时 MANIFEST.MF文件不记录的时间戳版本 -->
<useUniqueVersions>false</useUniqueVersions>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
<manifestEntries>
<Class-Path>.</Class-Path>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<type>jar</type>
<includeTypes>jar</includeTypes>
<outputDirectory>
${project.build.directory}/lib
</outputDirectory>
<excludeTransitive>false</excludeTransitive>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<stripVersion>false</stripVersion>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>../../dubbo-config/config/</directory>
</resource>
<!--recources文件夹下的所有文件都打进jar包-->
<resource>
<targetPath>${project.build.directory}/classes</targetPath>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<!-- 由于com.alibaba.dubbo.container.Main默认寻找META-INF下的applicationContext.xml,固将 applicationContext.xml文件拷贝到META-INF目录下-->
<resource>
<targetPath>${project.build.directory}/classes/META-INF/spring</targetPath>
<directory>src/main/resources/spring</directory>
<filtering>true</filtering>
<includes>
<include>applicationContext.xml</include>
</includes>
</resource>
</resources>
</build>
最后看一下dubbo的配置参数:
#服务注册中心(如果注册中心为集群,这里需要使用“,”分隔)
dubbo.user.registry.server=127.0.0.1:2181
#服务暴露端口
dubbo.user.protocol.port=20080
#请求超时时间
dubbo.user.timeout=3000
#请求失败重试次数
dubbo.user.retries=0
#客户端(如果订阅的服务为集群,这里需要使用“,”分隔)
dubbo.user.client=127.0.0.1:2181
到此,一个简单的dubbo服务就搭建好了,我们启动一下看看。
直接运行类UserProviderMock.java
如果看到"user provider start..."就证明启动成功了!
下面我们来看一下dubbo的客户端。
三、dubbo客户端
1、dubbo客户端配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="fund-baby-reference"/>
<!-- 使用zookeeper注册中心暴露服务地址,如果使用多个服务,需要配置多个客户端 -->
<dubbo:registry id="account-client" protocol="zookeeper" address="${dubbo.user.client}" />
<!-- 设置所有服务的启动时检查 (依赖服务不可用时会抛出异常): -->
<dubbo:consumer check="true" />
<dubbo:annotation/>
</beans>
然后将其import进入applicationContext.xml即可。
先跑一下单元测试。
2、单元测试
package com.sanjinbest.dubbo.mock;
import com.alibaba.dubbo.config.annotation.Reference;
import com.sanjinbest.dubbo.user.api.IUserInfoService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* <li></li>
*
* @author lixin
* @create 2017/9/18
*/
@RunWith(SpringJUnit4ClassRunner.class) //使用junit4进行测试
@ContextConfiguration(locations={"classpath*:spring/applicationContext.xml"}) //加载配置文件
public class MockDubbo {
@Reference
private IUserInfoService iUserInfoService;
@Test
public void test(){
System.out.println("Hello : " + iUserInfoService.getName());
System.out.println("age : " + iUserInfoService.getAge());
}
}
image.png
3、与spring mvc结合
完成了单元测试,在尝试与spring mvc进行结合使用。与spring mvc的结合用其实非常简单,spring mvc的配置不变,只需要将spring-mvc与dubbo配置都交给applictionContext.xml进行管理就好。
spring-mvc.xml
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<!-- 开启注解 -->
<mvc:annotation-driven/>
<!-- 定义跳转的文件的前后缀 ,视图模式配置-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 这里的配置我的理解是自动给后面action的方法return的字符串加上前缀和后缀,变成一个 可用的url地址 -->
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
applictionContext.xml
<?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
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath*:dubbo.properties</value>
</list>
</property>
</bean>
<!-- dubbo 服务客户端 -->
<import resource="classpath*:spring/fund-baby-reference.xml" />
<!-- spring mvc 配置 -->
<import resource="classpath*:spring/spring-mvc.xml" />
<!-- 开启注解 -->
<context:annotation-config/>
<!--自动扫描-->
<context:component-scan base-package="com.sanjinbest.dubbo" />
</beans>
web.xml
<?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/javaee
http://www.springmodules.org/schema/cache/springmodules-cache.xsd
http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
metadata-complete="true">
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 此处直接加载applicationContext.xml spring-mvc.xml在applicationContext.xml内加载 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<display-name>Archetype Created Web Application</display-name>
</web-app>
在看一下测试的controller和service
package com.sanjinbest.dubbo.controller;
import com.sanjinbest.dubbo.service.IndexService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* <li></li>
*
* @author lixin
* @create 2017/11/6
*/
@Controller
public class IndexController{
@Autowired
private IndexService indexService;
@RequestMapping(value="/user",method = RequestMethod.GET)
@ResponseBody
public String user(){
return indexService.userInfo();
}
}
controller的写法没有特殊变化,主要看一下service的。
package com.sanjinbest.dubbo.service;
import com.alibaba.dubbo.config.annotation.Reference;
import com.sanjinbest.dubbo.user.api.IUserInfoService;
import org.springframework.stereotype.Service;
/**
* <li></li>
*
* @author lixin
* @create 2017/11/6
*/
@Service
public class IndexService {
@Reference
private IUserInfoService iUserInfoService;
public String userInfo(){
return iUserInfoService.getName() + "," + iUserInfoService.getAge();
}
}
service这里注入dubbo接口,需要使用注解@Reference
启动后看一下测试结果:
最后,我们看一下dubbo-admin后台。
dubbo-admin.war可以在dubbo的官网上下载,下载后放到tomcat webapps内,解压war包,修改配置文件webapps/dubbo-admin/WEB-INF/dubbo.properties
设置dubbo.registry.address参数,我使用的是本地的zookeeper。
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest
启动tomcat,然后请求地址http://127.0.0.1:8080/dubbo-admin/
用户名和密码都是root。
dubbo-admin的功能有很多,就不作详细说明了,到此一个使用dubbo服务的spring mvc示例就介绍结束。
本文只对dubbo的搭建作一个初步的介绍,后续会有一些dubbo的线上使用介绍。
示例项目下载地址 : https://pan.baidu.com/s/1nvj3XpB 密码: 46vr
写的比较粗糙,可能会存在一些错误。欢迎大家反馈交流。