赞
踩
我们都知道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,也可以按照上面的过程走一遍即可。
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; } }
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); } }
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); } }
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; } }
假设有以下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; } }
启动服务后,向服务发起一个请求:
url:http://localhost/UseService/getUserById
body:{"userName":"我的名字"}
则返回以下内容:
{"userId":1, "userName":"我的名字"}
可以参考前面的一篇博文 如何在SpringBoot中做参数校验
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。