详解Java是如何通过接口来创建代理并进行http请求
场景
现在想要做这么一个事情,公司的dubbo服务都是内网的,但是提供了一个对外的出口,通过链接就能请求到对应的dubbo服务。(具体怎么做的应该就是个网关,然后将http请求转为dubbo请求,通过泛化调用去进行调用。代码看不到。)现在为了方便测试,我需要将配置的接口,通过http请求去请求对应的链接。
分析
项目的思想其实跟mybatis-spring整合包的思想差不多,都是生成代理去执行接口方法。
https://www.jb51.net/article/153378.htm
项目是个简单的spring项目就行了,然后项目引入项目的api,然后通过配置对应的服务名称,通过spring生成代理,注入spring容器,然后执行方法就是根据对应的域名+接口全路径+方法名去进行请求,参数是json。为了方便项目使用了hutool工具类,直接使用fastjson去进行序列化。
操作
首先创建工厂bean,就是用来返回代理的FactoryBean
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.reflect.Proxy;
public class HttpProxyFactoryBean<T> implements FactoryBean<T> {
@Autowired
private HttpProxyInvocationHandler httpProxyInvocationHandler;
private Class<T> rpcInterface;
public HttpProxyFactoryBean(Class<T> rpcInterface){
this.rpcInterface = rpcInterface;
}
@Override
public T getObject() throws Exception {
//这里应该放ComputerService接口
return (T)Proxy.newProxyInstance(rpcInterface.getClassLoader(),new Class[]{rpcInterface} ,httpProxyInvocationHandler);
}
@Override
public Class<?> getObjectType() {
return rpcInterface;
}
}
每一个动态代理类都必须要实现InvocationHandler这个接口
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
我们可以直接将实现InvocationHandler的实现类注入spring容器中,然后每一个接口走同一个innvoke方法,当然也可以每一个都new一个,然后可以在构造方法中塞入特定的一些参数。我这边因为对应的每一个代理没啥特殊的就走同一个了:
定义一些参数,请求的urlproxy.serverUrl,和请求添加的项目,proxy.project
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@Component
public class HttpProxyInvocationHandler implements InvocationHandler {
@Value("${proxy.serverUrl}")
private String serverUrl;
@Value("${proxy.project}")
private String serverProject;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> declaringClass = method.getDeclaringClass();
if (Object.class.equals(declaringClass)) {
return method.invoke(this, args);
}
String methodName = method.getName();
String name = method.getDeclaringClass().getName();
//拼接请求地址
String url = serverUrl + name + "/" + methodName;
// String url = "http://test:8080/soa/com.rdd.TestService/createActivity";
HashMap<String, String> paramMap = new HashMap<>();
// String result = HttpRequest.post(url).headerMap(paramMap, true).body("[" + JSONObject.toJSONString(args) + "]").execute().body();
String result = HttpRequest.post(url).headerMap(paramMap, true).body(JSONObject.toJSONString(args)).execute().body();
System.out.println(">>>" + url + "的响应结果为:" + result);
//将响应结果转换为接口方法的返回值类型
Class<?> returnType = method.getReturnType();
if (returnType.isPrimitive() || String.class.isAssignableFrom(returnType)) {
if (returnType == int.class || returnType == Integer.class) {
return Integer.valueOf(result);
} else if (returnType == long.class || returnType == Long.class) {
return Long.valueOf(result);
}
return result;
} else if (Collection.class.isAssignableFrom(returnType)) {
return JSONArray.parseArray(result, Object.class);
} else if (Map.class.isAssignableFrom(returnType)) {
return JSON.parseObject(result, Map.class);
} else {
return JSONObject.parseObject(result, returnType);
}
}
}
最后后将对应的工厂bean封装成bean定义,注入到spring容器中
我们的接口一般都是jar形式的,我就简单的写在一个proxy.txt文件中,然后去读取对应的接口全路径,注入到spring容器中,当然也可以通过扫描某个包,自定义注解等等方式实现。
import cn.hutool.core.io.file.FileReader;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Component
@PropertySource("classpath:application.properties")
public class HttpProxyRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
//默认UTF-8编码,可以在构造中传入第二个参数做为编码
FileReader fileReader = new FileReader("proxy.txt");
List<String> classStrList = fileReader.readLines();
Set<Class<?>> proxyClazzSet = new HashSet<>();
for (String s : classStrList) {
if (StringUtils.isBlank(s)) {
continue;
}
try {
Class<?> aClass = Class.forName(s);
proxyClazzSet.add(aClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
for (Class<?> targetClazz : proxyClazzSet) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(targetClazz);
GenericBeanDefinition definition = (GenericBeanDefinition) beanDefinitionBuilder.getRawBeanDefinition();
//设置构造方法的参数 对于Class<?>,既可以设置为Class,也可以传Class的完全类名
//definition.getConstructorArgumentValues().addGenericArgumentValue(targetClazz);
definition.getConstructorArgumentValues().addGenericArgumentValue(targetClazz.getName());
//Bean的类型,指定为某个代理接口的类型
definition.setBeanClass(HttpProxyFactoryBean.class);
//表示 根据代理接口的类型来自动装配
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
beanDefinitionRegistry.registerBeanDefinition(targetClazz.getName(),definition);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
到此这篇关于详解Java是如何通过接口来创建代理并进行http请求的文章就介绍到这了,更多相关java创建代理进行http请求内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341