지돌이의 블로그 입니다!

spring-boot-rest-http-invoker

기존의 HttpInvoker는 객체를 Serialize할 때 JavaSerializer을 사용합니다.

고로 JAVA 9 이전에서는 서로 다른 버전의 JVM이거나 사소한 객체의 버전이 다른 경우 정상적으로 동작하지 않습니다.

spring-boot-rest-http-invoker는 JavaSerializer 대신에 Jackson ObjectMapper를 사용하여 Restful API로 변환합니다.

RemoteInvocation 형식을 맞춘다면 PHP나 다른 언어에서도 동일하게 접근할 수 있습니다.

프로토콜 형식에 대해서는 아래를 참고해 주세요.

github : https://github.com/jc-lab/spring-boot-rest-http-invoker

bintray : https://bintray.com/jc-lab/spring.boot/spring-boot-rest-http-invoker

Maven


<dependency>

  <groupId>kr.jclab.spring</groupId>

  <artifactId>spring-boot-rest-http-invoker</artifactId>

  <version>(bintray에서 최신버전 확인)</version>

  <type>pom</type>

</dependency>

Gradle


implementation 'kr.jclab.spring:spring-boot-rest-http-invoker:(bintray에서 최신버전 확인)'

예제 소스


import com.fasterxml.jackson.databind.ObjectMapper;

import com.zeronsoftn.demo.demo1rpc.controller.TestContoller;

import kr.jclab.spring.resthttpinvoker.RestHttpInvokerProxyFactoryBean;

import kr.jclab.spring.resthttpinvoker.RestHttpInvokerServiceExporter;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;

import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;



@Configuration

public class RPCConfig {

    // Invoker 서버에서

    @Bean("/api/test")

    HttpInvokerServiceExporter apiExporter(TestService testService) {

        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.registerSubtypes(TestContoller.Test.class);

        RestHttpInvokerServiceExporter exporter = new RestHttpInvokerServiceExporter();

        exporter.setObjectMapper(objectMapper);

        exporter.setService(testService);

        exporter.setServiceInterface(TestService.class);

        return exporter;

    }



    // Invoker Client에서

    @Bean

    HttpInvokerProxyFactoryBean testService() {

        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.registerSubtypes(TestContoller.Test.class);

        RestHttpInvokerProxyFactoryBean factoryBean = new RestHttpInvokerProxyFactoryBean();

        factoryBean.setObjectMapper(objectMapper);

        factoryBean.setServiceUrl("http://127.0.0.1:8080/api/test");

        factoryBean.setServiceInterface(TestService.class);

        return factoryBean;

    }



}


예제 Interface


public interface TestService {

    void test_1();

    void test_2(int a);

    void test_3(String b);

    void test_4(Map<String, String> c);

    int test_5(int a, int b);

}

void test1() 실행시


POST /api/test HTTP/1.1

Content-Type: application/json

Accept-Language: ko-KR

Accept-Encoding: gzip

User-Agent: Java/1.8.0_191

Host: 127.0.0.1:9999

Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive

Content-Length: 76



{"methodName":"test_1","parameterTypes":[],"arguments":[],"attributes":null}

HTTP/1.1 200 

Content-Type: application/json

Content-Length: 31

Date: Thu, 28 Feb 2019 01:27:24 GMT



{"value":null,"exception":null}

void test_2(10) 실행시


POST /api/test HTTP/1.1

Content-Type: application/json

Accept-Language: ko-KR

Accept-Encoding: gzip

User-Agent: Java/1.8.0_191

Host: 127.0.0.1:9999

Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive

Content-Length: 83



{"methodName":"test_2","parameterTypes":["int"],"arguments":[10],"attributes":null}

HTTP/1.1 200 

Content-Type: application/json

Content-Length: 31

Date: Thu, 28 Feb 2019 01:27:24 GMT



{"value":null,"exception":null}

void test_3("20") 실행시


POST /api/test HTTP/1.1

Content-Type: application/json

Accept-Language: ko-KR

Accept-Encoding: gzip

User-Agent: Java/1.8.0_191

Host: 127.0.0.1:9999

Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive

Content-Length: 98



{"methodName":"test_3","parameterTypes":["java.lang.String"],"arguments":["20"],"attributes":null}

HTTP/1.1 200 

Content-Type: application/json

Content-Length: 31

Date: Thu, 28 Feb 2019 01:27:24 GMT



{"value":null,"exception":null}

void test_4(Map<String, Object> map) 실행시


public static class Test {

        public int a;

        public int b;



        public Test() {}



        public Test(int a, int b) {

            this.a = a;

            this.b = b;

        }

    }



    @RequestMapping(path = "/test-1")

    @ResponseBody

    public String test1() {

        HashMap<String, Object> test = new HashMap<>();

        test.put("a", "aaaa");

        test.put("b", 123123);

        test.put("c", 3.14);

        test.put("d", new Test(10, 20));

        apiProxy.test_4(test);

        return "OK : " + apiProxy.test_5(22, 55);

    }

POST /api/test HTTP/1.1

Content-Type: application/json

Accept-Language: ko-KR

Accept-Encoding: gzip

User-Agent: Java/1.8.0_191

Host: 127.0.0.1:9999

Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive

Content-Length: 143



{"methodName":"test_4","parameterTypes":["java.util.Map"],"arguments":[{"a":"aaaa","b":123123,"c":3.14,"d":{"a":10,"b":20}}],"attributes":null}

HTTP/1.1 200 

Content-Type: application/json

Content-Length: 31

Date: Thu, 28 Feb 2019 01:47:29 GMT



{"value":null,"exception":null}

int test_5(22, 55) 실행시


POST /api/test HTTP/1.1

Content-Type: application/json

Accept-Language: ko-KR

Accept-Encoding: gzip

User-Agent: Java/1.8.0_191

Host: 127.0.0.1:9999

Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive

Content-Length: 92



{"methodName":"test_5","parameterTypes":["int","int"],"arguments":[22,55],"attributes":null}

HTTP/1.1 200 

Content-Type: application/json

Content-Length: 29

Date: Thu, 28 Feb 2019 01:47:29 GMT



{"value":77,"exception":null}

Comment +2

  • 2019.04.04 17:47

    비밀댓글입니다

    • 안녕하세요. 덧글이 늦어 죄송합니다^^
      일단 spring-boot-rest-http-invoker는 기존의 HttpInvoker는 객체를 Serialize할 때 JavaSerializer을 사용하기 때문에 JAVA 9 이전에서는 서로 다른 버전의 JVM이거나 사소한 객체의 버전이 다른 경우 정상적으로 동작하지 않는 문제를 해결하기 위하여, 또한 다른 언어와 API 호환을 위하여 만든 라이브러리 입니다.

      payload전달에 대해 JavaSerializer대신 json형식으로 전달하는 방식으로 인하여 발행하는 단점으로는 모든 Java개체를 Serialize할 수는 없다는 것이 단점이긴 합니다.
      예를들어 final 객체라든가 geter가 없는 private 필드가 존재하거나 @JsonCreator없이 NoArgsConstructor가 없는 클래스라든가 복잡한 객체(Type이 명시되지 않은 Object형 타입으로 선언되어있는 클래스 등)에 대해서는 Serialize가 불가하여 오류가 발생할 수 있습니다.
      따라서 이러한 객체를 사용하지 않도록 사용자가 Parmeter로 들어가는 클래스와 Return되는 Class, Throwable되는 Class에 대해 Jackson으로 Serialize/Deserialize될 수 있도록 만들어진 클래스를 사용해야 합니다.