Java Spring MVC

基于注解方式的dubbo+spring mvc整合

2017-11-06  本文已影响65人  木子小三金

最近项目使用了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

image.png

如果看到"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
启动后看一下测试结果:

image.png

最后,我们看一下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

写的比较粗糙,可能会存在一些错误。欢迎大家反馈交流。

上一篇下一篇

猜你喜欢

热点阅读