当前位置:   article > 正文

springboot 将@Service映射成为Restful服务_spring 如何将另外一个服务的端口映射成接口

spring 如何将另外一个服务的端口映射成接口

需求

我们都知道springboot中使用 @RestController 标签可以方便的开发一个restful的微服务,在当今格技术栈混搭的情况下,有这么个需求:
@Service 所代表的核心逻辑服务可以打成jar包供本地调用,也可以发布成一个微服务以web的形式提供restful接口,后者的话,用 @RestController 包一层即可,但是懒惰的程序员可不想做这种无聊的事情,只是为了发布web服务,而写一堆繁琐的service包装代码,有没有一种方法,让 @Servcie 自动包装成web服务呢?

思路

先看看@RestController是怎么实现的:
RequestMappingHandlerMapping将带有@ReqestMapping和@Controller注解的类添加到url映射中;当这些url被请求时, 从HandlerMethodArgumentResolverComposite里找到合适的HandlerMethodArgumentResolver进行参数的解析,使其对应到映射的Controller方法参数上;Controller里的方法执行结束后,从HandlerMethodReturnValueHandlerComposite里找到合适的HandlerMethodReturnValueHandler来进行返回值的包装。
所以针对@Servcie,也可以按照上面的过程走一遍即可。

方法

1 定义Service处理映射

ServiceHandlerMapping.java

import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;

import java.lang.reflect.Method;

public class ServiceHandlerMapping extends RequestMappingInfoHandlerMapping {

    private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();

    /**
     * 支持@Service注解的类
     * @param beanType
     * @return
     */
    @Override
    protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Service.class));
    }

    /**
     * 将Service类里面的方法按照 /SimpleClassName/methodName 的方式去注册RequestMapping
     * @param method
     * @param handlerType
     * @return
     */
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo.Builder builder = RequestMappingInfo
                .paths(String.join("/", handlerType.getSimpleName(), method.getName()))
                .consumes(MediaType.APPLICATION_JSON_VALUE)
                .methods(RequestMethod.POST);
        return builder.options(this.config).build();
    }

    /**
     * 保证自定义的映射优先处理
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
  • 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

2 定义参数解析器

ServiceMethodArgumentResolver.java

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Service;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;

import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST;

public class ServiceMethodArgumentResolver implements HandlerMethodArgumentResolver {
    private static final String SERVICE_ARGS = "service_args";
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getMethod().getDeclaringClass().getDeclaredAnnotation(Service.class) != null;
    }

    /**
     * 参数解析
     * 第一次将请求内容解析成map, 并保存到request中,下个参数解析直接从map里面取
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws IOException {
        Map<String, Object> serviceArgs = (Map<String, Object>) webRequest.getAttribute(SERVICE_ARGS, SCOPE_REQUEST);
        if (serviceArgs == null) {
            HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
            serviceArgs = objectMapper.readValue(servletRequest.getInputStream(), ModelMap.class);
            webRequest.setAttribute(SERVICE_ARGS, serviceArgs, SCOPE_REQUEST);
        }
        Object parameterValueRaw = serviceArgs.get(parameter.getParameterName());
        if (parameterValueRaw == null) {
            return null;
        }
        String parameterValueStr = objectMapper.writeValueAsString(parameterValueRaw);
        JavaType javaType = objectMapper.getTypeFactory().constructType(GenericTypeResolver.resolveType(parameter.getGenericParameterType(), parameter.getContainingClass()));
        return objectMapper.readValue(parameterValueStr, javaType);
    }
}
  • 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

3 定义结果包装器

ServiceMethodReturnValueHandler.java

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletResponse;

public class ServiceMethodReturnValueHandler implements HandlerMethodReturnValueHandler {

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return returnType.getMethod().getDeclaringClass().getDeclaredAnnotation(Service.class) != null;
    }

    /**
     * 将结果封装成json格式
     */
    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        response.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
        objectMapper.writeValue(response.getWriter(), returnValue);
        mavContainer.setRequestHandled(true);
    }
}
  • 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

4 配置组件

ServiceRequestMappingConfiguration.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class ServiceRequestMappingConfiguration {

    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @Bean
    public ServiceHandlerMapping serviceHandlerMapping() {
        return new ServiceHandlerMapping();
    }

    @Bean
    public ServiceMethodArgumentResolver serviceMethodArgumentResolver() {
        ServiceMethodArgumentResolver serviceMethodArgumentResolver = new ServiceMethodArgumentResolver();

        // 将此参数解析器的优先级提到最高
        List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
        argumentResolvers.add(serviceMethodArgumentResolver);
        argumentResolvers.addAll(requestMappingHandlerAdapter.getArgumentResolvers());
        requestMappingHandlerAdapter.setArgumentResolvers(argumentResolvers);
        return serviceMethodArgumentResolver;
    }

    @Bean
    public ServiceMethodReturnValueHandler serviceMethodReturnValueHandler() {
        ServiceMethodReturnValueHandler serviceMethodReturnValueHandler = new ServiceMethodReturnValueHandler();

        // 将此结果包装器的优先级提到最高
        List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
        returnValueHandlers.add(serviceMethodReturnValueHandler);
        returnValueHandlers.addAll(requestMappingHandlerAdapter.getReturnValueHandlers());
        requestMappingHandlerAdapter.setReturnValueHandlers(returnValueHandlers);
        return serviceMethodReturnValueHandler;
    }
}
  • 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

5 使用

假设有以下Servcie代码:
UserService.java

import org.springframework.stereotype.Service;


@Service
public class UserService {

    private static int i = 0;

    @Override
    public User getUserById(String userName) {
        User user = new User();
        user.setId(++i);
        user.setName(userName);
        return user;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

启动服务后,向服务发起一个请求:

url:http://localhost/UseService/getUserById
body:{"userName":"我的名字"}

则返回以下内容:
{"userId":1, "userName":"我的名字"}

其他问题

参数校验

可以参考前面的一篇博文 如何在SpringBoot中做参数校验

注册中心

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/580097
推荐阅读
相关标签
  

闽ICP备14008679号