java进行http请求 详解Java是怎样通过接口来创建代理并进行http请求
你就像甜甜的益达 人气:1场景
现在想要做这么一个事情,公司的dubbo服务都是内网的,但是提供了一个对外的出口,通过链接就能请求到对应的dubbo服务。(具体怎么做的应该就是个网关,然后将http请求转为dubbo请求,通过泛化调用去进行调用。代码看不到。)现在为了方便测试,我需要将配置的接口,通过http请求去请求对应的链接。
分析
项目的思想其实跟mybatis-spring整合包的思想差不多,都是生成代理去执行接口方法。
https:
项目是个简单的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; /** * @Title:相对于BeanFactory这个大工厂,这是一个小工厂,专门用于创建某种类型的bean(默认创建的是单例bean) * @Description 创建代理对象 * @Version */ public class HttpProxyFactoryBean<T> implements FactoryBean<T> { /** *【注意】 * 这里之所以可以进行自动装配,是因为当前的这个HttpProxyFactoryBean是会被注册到Spring中的 * 只不过它的注册方式 跟一般的不一样(一般会在类上,加一个如@Component、@Service这样的注解 ) * 它是通过注册BeanDefinition的方式注册的,可能会注册多个,而其中的每一个HttpProxyFactoryBean实例都会被自动装配同一个HttpProxyInvocationHandler实例 * * 也有等价的做法是: * 利用ApplicationContextAware接口的setApplicationContext获取到applicationContext, * 然后把applicationContext 作为属性设置到当前类中 * 再利用applicationContext的getBean方法来获取InvocationHandler的实例 */ @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; /** * @Description 把服务方法的调用转换为对远程服务的http请求 * @Version */ @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; /** * @Title:bean工厂的后置处理器,用于动态注册bean * @Date 2021/3/23 10:13 * @Description * @Version */ @Component @PropertySource("classpath:application.properties") public class HttpProxyRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { /** * 该方法用来注册更多的bean到spring容器中 * * @param beanDefinitionRegistry * @throws BeansException */ @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 { } }
加载全部内容