1.服务提供者实现(Controller接口)
package com.vz.controller.controller;
import cn.hutool.core.util.StrUtil;
import com.vz.common.constant.ErrorEnum;
import com.vz.common.exception.ServiceException;
import com.vz.common.util.R;
import com.vz.service.UserService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.springframework.web.bind.annotation.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
/**
* @author visy.wang
* @date 2020/12/18 16:52
*/
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/invoke")
public class InvokeController {
private static final Gson GSON = new Gson();
private final UserService userService;
//忽略的方法名前缀
private static final String LAMBDA_PREFIX = "lambda$";
private static final String CGLIB_PREFIX = "CGLIB$";
//反射实体及方法缓存
private static Map<String, Object> servicesCache = Maps.newHashMap();
private static Map<String, Method> methodsCache = Maps.newHashMap();
@PostMapping("/invoke/{service}/{method}")
public R<String> doInvoke(
@PathVariable("service") String service,
@PathVariable("method") String method,
@RequestBody String params) {
log.info("调用入参:{}.{}: {}", service, method, params);
initServiceMethodCache();
Object serviceObj = servicesCache.get(service);
if(serviceObj == null){
return new R<>("服务不存在", ErrorEnum.PARAM_ERROR.getCode());
}
String methodPath = service+"."+method;
Method m;
if((m = methodsCache.get(methodPath)) == null){
//R 是自定义的返回体(包含code, data, msg属性)
return new R<>("方法不存在", ErrorEnum.PARAM_ERROR.getCode());
}
Class<?>[] paramTypes = m.getParameterTypes();
try{
List<Object> paramList = Lists.newArrayList();
if(StrUtil.isNotBlank(params)){
JSONArray jsonArr = new JSONArray(params);
for(int i=0; i<jsonArr.length(); i++){
String param = jsonArr.get(i).toString();
paramList.add(GSON.fromJson(param, paramTypes[i]));
}
}
String result = GSON.toJson(m.invoke(serviceObj, paramList.toArray()));
return new R<>(result);
}catch (Exception e){
if(e instanceof InvocationTargetException
&& ((InvocationTargetException)e).getTargetException() instanceof ServiceException){
ServiceException serviceException
= (ServiceException)(((InvocationTargetException)e).getTargetException());
// 业务异常单独处理, 我的业务异常是ServiceException,
// 可结合自己项目的业务异常改写 catch里的内容
return new R<>(serviceException.getMessage(), serviceException.getErrorCode());
}else{
return new R<>("方法调用出错:"+e.getMessage(), ErrorEnum.SYSTEM_ERROR.getCode());
}
}
}
//反射相关变量缓存
private void initServiceMethodCache(){
if(!servicesCache.isEmpty()){
return;
}
Class<?> clazz = userService.getClass();
servicesCache.put("userService", userService);
for(Method m: clazz.getDeclaredMethods()){
m.setAccessible(true);
methodsCache.put(getMethodPath("userService", m), m);
}
}
private String getMethodPath(String serviceName, Method m){
//末尾拼接参数数量,用以区分参数个数不同的重载函数
//参数个数相同的重载函数暂无法区分,出现的话会覆盖,可自行提供实现
String name = m.getName();
if(name.startsWith(CGLIB_PREFIX) || name.startsWith(LAMBDA_PREFIX)){
return null;
}
return serviceName + "." + name + "_" + m.getParameters().length;
}
}
2.Feign接口实现
package com.vz.feign;
import com.vz.feign.factory.RemoteServiceFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author visy.wang
* @date 2020/12/18 14:50
*/
@FeignClient(name = "服务提供方在注册中心的名称", fallbackFactory = RemoteServiceFactory.class)
public interface RemoteService {
/**
* 方法统一调用接口
* @param method 方法名
* @param params 参数
* @return 返回结果
*/
@PostMapping("/invoke/invoke/{service}/{method}")
String invoke(@PathVariable("service") String service,
@PathVariable("method") String method,
@RequestBody String params);
}
//以下是Feign调用异常处理(非必须)
package com.vz.feign.factory;
import com.vz.feign.RemoteService;
import com.vz.feign.fallback.RemoteServiceFallBackImpl;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* @author visy.wang
* @date 2020/12/18 14:53
*/
@Component
public class RemoteServiceFactory implements FallbackFactory<RemoteService> {
@Override
public RemoteService create(Throwable throwable) {
RemoteServiceFallBackImpl remoteServiceFallBack = new RemoteServiceFallBackImpl();
remoteServiceFallBack.setCause(throwable);
return remoteServiceFallBack;
}
}
package com.vz.feign.fallback;
import com.vz.common.constant.ErrorEnum;
import com.vz.common.util.R;
import com.vz.feign.RemoteService;
import com.google.gson.Gson;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author visy.wang
* @date 2020/12/18 14:52
*/
@Component
@Slf4j
public class RemoteServiceFallBackImpl implements RemoteService {
private static final Gson GSON = new Gson();
@Setter
private Throwable cause;
@Override
public String invoke(String service, String method, String params) {
return GSON.toJson(new R<>(cause.getMessage(), ErrorEnum.SYSTEM_ERROR.getCode()));
}
}
3.调用:
package com.vz.service.impl;
import com.vz.common.exception.ServiceException;
import com.vz.common.util.R;
import com.vz.feign.RemoteService;
import com.vz.service.RemoteServiceAdapter ;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
/**
* @author visy.wang
* @date 2020/12/18 17:48
*/
@Service("remoteServiceAdapter ")
@AllArgsConstructor
public class RemoteServiceAdapter implements RemoteServiceAdapter {
private RemoteService remoteService;
private static final Gson GSON = new Gson();
private static final String SUCCESS = "200";
/**
* serviceName: 服务名称
* methodName:方法名
* clazz:返回类型Class
* args: 方法参数
*/
@Overrride
public<E> E invoke(String serviceName, String methodName, Class<E> clazz, Object... args){
String result = remoteService.invoke(serviceName, methodName+"_"+args.length, GSON.toJson(args));
R r = GSON.fromJson(result, R.class);
if(SUCCESS.equals(r.getCode().toString())){
return GSON.fromJson(String.valueOf(r.getData()), clazz);
}
throw new ServiceException(r.getMsg(), r.getCode());
}
}
在Controller调用
User user = new User();
//user.setXXX ...
//java.lang.Void代表方法没有返回值
remoteServiceAdapter.invoke("userService", "saveUser", Void.class, user );
Long userId = 100L;
User saveUser = remoteServiceAdapter.invoke("userService", "getById", User.class, userId);