最近公司有个新的需求,写了一个接口,想要把服务暴露出去,但是这个服务所在的进程是非web项目,(可以理解成schedule/batch等进程项目),所以没有tomcat等容器,而且只有这一个服务,无论是加dubbo服务还是加tomcat等容器都显得复杂了。那么应该如何将服务暴露出去?

经过网上搜索后,最终解决问题,记录在此。

为了快速搭建,使用springboot来搭建项目:

项目结构如图:

基于spring http服务远程调用_java

 

 首先需要创建一个接口,服务的提供者和服务的调用方都依赖这个模块。

package com.xiazhi.spring.service.api;

import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

/**
 * @author 赵帅
 * @date 2020/6/8
 */
public interface IUserService {

    /**
     * 获取姓名
     * @return 姓名
     */
    String getName();

    /**
     * 根据姓名获取年龄
     * @param name 姓名
     * @return 年龄
     */
    @NotNull
    Integer getAge(@Nullable String name);
}

然后在service模块中,实现接口作为服务的提供方,需要依赖的jar包有:

基于spring http服务远程调用_tomcat_02基于spring http服务远程调用_java_03
    <dependencies>
        <dependency>
            <groupId>com.xiazhi</groupId>
            <artifactId>spring-service-api</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
    </dependencies>
View Code

配置文件需要加上:

spring.main.web-application-type=none

实现接口:

package com.xiazhi.spring.service.impl;

import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;
import com.xiazhi.spring.service.api.IUserService;
import org.springframework.stereotype.Service;

/**
 * @author 赵帅
 * @date 2020/6/8
 */
@Service
public class UserServiceImpl implements IUserService {

    @Override
    public String getName() {
        return "张一";
    }

    @Override
    @NotNull
    public Integer getAge(@Nullable String name) {
        if ("".equals(name)) {
            return 10;
        }
        return 18;
    }
}

暴露服务:

package com.xiazhi.spring.service.config;

import com.sun.net.httpserver.HttpHandler;
import com.xiazhi.spring.service.api.IUserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter;
import org.springframework.remoting.support.SimpleHttpServerFactoryBean;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 赵帅
 * @date 2020/6/8
 */
@Configuration
public class HttpInvokerConfiguration {

    private final IUserService userService;
    private final TaskExecutor taskExecutor;

    public HttpInvokerConfiguration(IUserService userService, TaskExecutor taskExecutor) {
        this.userService = userService;
        this.taskExecutor = taskExecutor;
    }

    /**
     * 将IUserService服务暴露出去
     */
    @Bean
    public SimpleHttpInvokerServiceExporter serviceExporter() {
        SimpleHttpInvokerServiceExporter exporter = new SimpleHttpInvokerServiceExporter();
        exporter.setService(userService);
        exporter.setServiceInterface(IUserService.class);
        return exporter;
    }

    /**
     * 为暴露的服务启用http服务
     * @return httpServer工厂类
     */
    @Bean
    public SimpleHttpServerFactoryBean serverFactoryBean() {
        SimpleHttpServerFactoryBean factoryBean = new SimpleHttpServerFactoryBean();
        Map<String, HttpHandler> map = new HashMap<>(2);
     map.put("/userService", serviceExporter()); factoryBean.setContexts(map); factoryBean.setPort(
9999); factoryBean.setExecutor(taskExecutor); return factoryBean; } }

启动服务。

然后是服务的调用方,依赖有:

基于spring http服务远程调用_tomcat_02基于spring http服务远程调用_java_03
<dependencies>
        <dependency>
            <groupId>com.xiazhi</groupId>
            <artifactId>spring-service-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
View Code

配置文件:

package com.xiazhi.spring.config;

import com.xiazhi.spring.service.api.IUserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;

/**
 * @author 赵帅
 * @date 2020/6/8
 */
@Configuration
public class HttpServiceConfiguration {

    /**
     * 使用http代理工厂调用服务
     * @return http代理工厂创建代理对象
     */
    @Bean
    public HttpInvokerProxyFactoryBean httpInvokerProxyFactoryBean() {
        HttpInvokerProxyFactoryBean factoryBean = new HttpInvokerProxyFactoryBean();
        factoryBean.setServiceUrl("http://localhost:9999/userService");
        factoryBean.setServiceInterface(IUserService.class);
        return factoryBean;
    }
}

使用接口,调用方法:

package com.xiazhi.spring.controller;

import com.xiazhi.spring.service.api.IUserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 赵帅
 * @date 2020/6/8
 */
@RestController
public class UserController {

    private final IUserService userService;

    public UserController(IUserService userService) {
        this.userService = userService;
    }

    @GetMapping("/test")
    public String test() {
        String name = userService.getName();
        Integer age = userService.getAge(null);
        System.out.println(String.format("姓名:[%s],age:[%s]", name, age));
        return name;
    }
}

运行,调用test路径测试调用结果。

项目完整结构:

基于spring http服务远程调用_spring_06