詳解Java是如何通過接口來創(chuàng)建代理并進行http請求
場景
現在想要做這么一個事情,公司的dubbo服務都是內網的,但是提供了一個對外的出口,通過鏈接就能請求到對應的dubbo服務。(具體怎么做的應該就是個網關,然后將http請求轉為dubbo請求,通過泛化調用去進行調用。代碼看不到。)現在為了方便測試,我需要將配置的接口,通過http請求去請求對應的鏈接。
分析
項目的思想其實跟mybatis-spring整合包的思想差不多,都是生成代理去執(zhí)行接口方法。
http://www.dbjr.com.cn/article/153378.htm
項目是個簡單的spring項目就行了,然后項目引入項目的api,然后通過配置對應的服務名稱,通過spring生成代理,注入spring容器,然后執(zhí)行方法就是根據對應的域名+接口全路徑+方法名去進行請求,參數是json。為了方便項目使用了hutool工具類,直接使用fastjson去進行序列化。
操作
首先創(chuàng)建工廠bean,就是用來返回代理的FactoryBean
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.annotation.Autowired; import java.lang.reflect.Proxy; /** * @Title:相對于BeanFactory這個大工廠,這是一個小工廠,專門用于創(chuàng)建某種類型的bean(默認創(chuàng)建的是單例bean) * @Description 創(chuàng)建代理對象 * @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; } }
每一個動態(tài)代理類都必須要實現InvocationHandler這個接口
每一個動態(tài)代理類都必須要實現InvocationHandler這個接口,并且每個代理類的實例都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發(fā)為由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工廠的后置處理器,用于動態(tài)注冊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 { } }
到此這篇關于詳解Java是如何通過接口來創(chuàng)建代理并進行http請求的文章就介紹到這了,更多相關java創(chuàng)建代理進行http請求內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot?使用?Sa-Token?完成注解鑒權功能(權限校驗)
Sa-Token?是一個輕量級?java?權限認證框架,主要解決登錄認證、權限認證、單點登錄、OAuth2、微服務網關鑒權?等一系列權限相關問題,這篇文章主要介紹了SpringBoot使用Sa-Token完成注解鑒權功能,需要的朋友可以參考下2023-05-05vscode搭建java開發(fā)環(huán)境的實現步驟
本文主要介紹了vscode搭建java開發(fā)環(huán)境,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-03-03Java并發(fā)系列之ReentrantLock源碼分析
這篇文章主要為大家詳細介紹了Java并發(fā)系列之ReentrantLock源碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-02-02