因为某些原因,原本注册在同一个nacos里的部分微服务需要拆分出去,而拆分出去的那部分服务调用方式需要修改。所以为了简单省事,加个了@HttpClient注解用来替换@FeignClient。
三步走:
1、@HttpClient注解
2、扫描被@HttpClient注解的接口
3、为扫描到的接口创建代理类
@HttpClient注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface HttpClient {
HttpUrl value();
}
url枚举类
@AllArgsConstructor
@Getter
public enum HttpUrl {
BAI_DU("https://www.baidu.com/", "百度"),
private String url;
private String desc;
}
扫描被@HttpClient注解的接口
@Component
public class HttpClientRegistryProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware {
private ResourceLoader resourceLoader;
private Environment environment;
private final RestTemplate restTemplate;
public HttpClientRegistryProcessor() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(5000);//ms
factory.setConnectTimeout(15000);//ms
restTemplate = new RestTemplate(factory);
}
@Override
public void postProcessBeanDefinitionRegistry(@NotNull BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(HttpClient.class));
Set<BeanDefinition> beanDefinitionHolders = scanner.findCandidateComponents("org.jeecg");
for (BeanDefinition holder : beanDefinitionHolders) {
registerHttpClientBean(holder, registry);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void registerHttpClientBean(BeanDefinition definition, BeanDefinitionRegistry registry) {
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) definition;
String beanClassName = beanDefinition.getBeanClassName();
try {
Class<?> targetClass = Class.forName(beanClassName);
if (targetClass.isInterface()) {
Object proxy = Proxy.newProxyInstance(
targetClass.getClassLoader(),
new Class[]{targetClass},
new HttpClientInvocationHandler(targetClass, restTemplate));
BeanDefinitionBuilder proxyBeanBuilder = BeanDefinitionBuilder.genericBeanDefinition(targetClass, (Supplier) () -> proxy);
AbstractBeanDefinition proxyBeanDefinition = proxyBeanBuilder.getRawBeanDefinition();
String beanName = BeanDefinitionReaderUtils.generateBeanName(definition, registry);
registry.registerBeanDefinition(beanName, proxyBeanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(@NotNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
// Do nothing
}
protected ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
@Override
protected boolean isCandidateComponent(@NotNull AnnotatedBeanDefinition beanDefinition) {
boolean isCandidate = false;
if (beanDefinition.getMetadata().isIndependent()) {
if (!beanDefinition.getMetadata().isAnnotation()) {
isCandidate = true;
}
}
return isCandidate;
}
};
}
@Override
public void setEnvironment(@NotNull Environment environment) {
this.environment = environment;
}
@Override
public void setResourceLoader(@NotNull ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
为扫描到的接口创建代理类
public class HttpClientInvocationHandler implements InvocationHandler {
private final RestTemplate restTemplate;
private final String baseUrl;
private static final List<MediaType> DEFAULT_MEDIA_TYPES = Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN);
public static final Method HASH_CODE;
public static final Method EQUALS;
public static final Method TO_STRING;
static {
Class<Object> object = Object.class;
try {
HASH_CODE = object.getDeclaredMethod("hashCode");
EQUALS = object.getDeclaredMethod("equals", object);
TO_STRING = object.getDeclaredMethod("toString");
} catch (NoSuchMethodException e) {
// Never happens.
throw new Error(e);
}
}
private static final CopyOptions copyOptions = new CopyOptions(null, true);
static {
Editor<String> fieldNameEditor = s -> {
if ("msg".equals(s)) {
return "message";
}
return s;
};
copyOptions.setFieldNameEditor(fieldNameEditor);
}
public HttpClientInvocationHandler(Class<?> targetClass, RestTemplate restTemplate) {
this.restTemplate = restTemplate;
HttpClient httpClientAnnotation = targetClass.getAnnotation(HttpClient.class);
if (httpClientAnnotation != null) {
this.baseUrl = httpClientAnnotation.value().getUrl();
} else {
this.baseUrl = null;
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.equals(HASH_CODE)) {
return objectHashCode(proxy);
}
if (method.equals(EQUALS)) {
return objectEquals(proxy, args[0]);
}
if (method.equals(TO_STRING)) {
return objectToString(proxy);
}
// 对接口默认方法的支持
if (method.isDefault()) {
Class<?> declaringClass = method.getDeclaringClass();
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true);
return constructor.
newInstance(declaringClass, MethodHandles.Lookup.PRIVATE).
unreflectSpecial(method, declaringClass).
bindTo(proxy).
invokeWithArguments(args);
}
HttpHeaders headers = new HttpHeaders();
headers.setAccept(DEFAULT_MEDIA_TYPES);
String url = resolveUrl(baseUrl, method);
Object body = null;
HttpMethod httpMethod = null;
if (method.isAnnotationPresent(GetMapping.class)) {
httpMethod = HttpMethod.GET;
} else if (method.isAnnotationPresent(PostMapping.class)) {
httpMethod = HttpMethod.POST;
} else if (method.isAnnotationPresent(PutMapping.class)) {
httpMethod = HttpMethod.PUT;
} else if (method.isAnnotationPresent(DeleteMapping.class)) {
httpMethod = HttpMethod.DELETE;
}
if (httpMethod == null) {
throw new RuntimeException("@HttpClient不支持的请求方式");
}
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(url);
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
RequestParam requestParamAnnotation = AnnotationUtils.findAnnotation(parameter, RequestParam.class);
if (requestParamAnnotation != null) {
String name = requestParamAnnotation.value();
name = !"".equals(name) ? name : parameter.getName();
uriComponentsBuilder.queryParam(name, args[i]);
continue; // 应该不可能同一个参数同时标注多个吧。
}
RequestBody requestBodyAnnotation = AnnotationUtils.findAnnotation(parameter, RequestBody.class);
if (requestBodyAnnotation != null) {
body = args[i]; // 应该没人会传两个body吧
continue;
}
RequestHeader requestHeaderAnnotation = AnnotationUtils.findAnnotation(parameter, RequestHeader.class);
if (requestHeaderAnnotation != null) {
String name = requestHeaderAnnotation.value();
name = !"".equals(name) ? name : parameter.getName();
headers.set(name, String.valueOf(args[i]));
continue;
}
}
HttpEntity<?> httpEntity = new HttpEntity<>(body, headers);
ResponseEntity<String> exchange = restTemplate.exchange(uriComponentsBuilder.toUriString(), httpMethod, httpEntity, String.class);
String res = exchange.getBody();
JSONObject jsonObject = JSON.parseObject(res);
Map<String, Object> innerMap = jsonObject.getInnerMap();
Class<?> returnType = method.getReturnType();
return BeanUtil.mapToBean(innerMap, returnType, false, copyOptions);
}
private String resolveUrl(String baseUrl, Method method) {
GetMapping getMapping = method.getAnnotation(GetMapping.class);
if (getMapping != null) {
String path = getMapping.value()[0];
return baseUrl + path;
}
PostMapping postMapping = method.getAnnotation(PostMapping.class);
if (postMapping != null) {
String path = postMapping.value()[0];
return baseUrl + path;
}
PutMapping putMapping = method.getAnnotation(PutMapping.class);
if (putMapping != null) {
String path = putMapping.value()[0];
return baseUrl + path;
}
DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
if (deleteMapping != null) {
String path = deleteMapping.value()[0];
return baseUrl + path;
}
throw new RuntimeException("@HttpClient不支持的请求方式");
}
public String objectClassName(Object obj) {
return obj.getClass().getName();
}
public int objectHashCode(Object obj) {
return System.identityHashCode(obj);
}
public boolean objectEquals(Object obj, Object other) {
return obj == other;
}
public String objectToString(Object obj) {
return objectClassName(obj) + '@' + Integer.toHexString(objectHashCode(obj));
}
}
使用方式
直接替换掉@FeignClient
//@FeignClient(name = "xxx-service")
@HttpClient(HttpUrl.BAI_DU)
标签:FeignClient,Http,String,class,public,return,null,method,请求
From: https://www.cnblogs.com/end-of-a-journey/p/17632700.html